Несколько подписок на push-уведомления, некоторые из них работают правильно, а некоторые нет

Я попытался опубликовать это на форуме разработчиков Exchange и не получил ответов, поэтому попробую здесь. Ссылка на форум

У меня есть службы Windows, которые запускаются каждые пятнадцать минут, чтобы увидеть, есть ли какие-либо подписки, которые необходимо создать или обновить. Я использую Managed API v1.1 против Exchange 2007 SP1. У меня есть таблица, в которой хранятся все пользователи, которым нужен мониторинг почтового ящика. Так что, когда уведомление поступает в «Службу прослушивания», я могу найти пользователя и получить доступ к сообщению, чтобы зарегистрировать его в приложении, которое мы создаем. В таблице у меня есть следующие столбцы, в которых хранится информация о подписке:

  1. Идентификатор подписки — VARCHAR(MAX)
  2. Водяной знак - VARCHAR(MAX)
  3. LastStatusUpdate – DATETIME

Мои службы вызывают функцию, которая запрашивает необходимые данные (в зависимости от того, какую функцию она выполняет). Если у пользователя еще нет подписки, служба создаст ее. Я использую олицетворение для доступа к почтовым ящикам. Вот мой метод «ActiveSubscription», который запускается, когда пользователю требуется создать или обновить подписку.

private void ActivateSubscription(User user)
{
  if (user.ADGUID.HasValue)
  {
    PrincipalContext ctx = new PrincipalContext(ContextType.Domain, Settings.ActiveDirectoryServerName, Settings.ActiveDirectoryRootContainer);

    using (UserPrincipal up = UserPrincipal.FindByIdentity(ctx, IdentityType.Guid, user.ADGUID.Value.ToString()))
    {
      ewService.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SID, up.Sid.Value);
    }
  }
  else
  {
    ewService.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, user.EmailAddress);
  }

  PushSubscription pushSubscription = ewService.SubscribeToPushNotifications(
    new FolderId[] { WellKnownFolderName.Inbox, WellKnownFolderName.SentItems },
    Settings.ListenerService, 30, user.Watermark,
    EventType.NewMail, EventType.Created);

  user.Watermark = pushSubscription.Watermark;
  user.SubscriptionID = pushSubscription.Id;
  user.SubscriptionStatusDateTime = DateTime.Now.ToLocalTime();

  _users.Update(user);
}

Мы также запустили следующий командлет, чтобы предоставить пользователю, к которому мы обращаемся к EWS, возможность олицетворения на сервере Exchange.

Get-ExchangeServer | where {$_.IsClientAccessServer -eq $TRUE} | ForEach-Object {Add-ADPermission -Identity $_.distinguishedname -User (Get-User -Identity mailmonitor | select-object).identity -extendedRight ms-Exch-EPI-Impersonation}

Приведенный выше код «ActivateSubscription» работает должным образом. Или я так думал. Когда я тестировал его, он контролировал мой почтовый ящик, и он отлично работал. Единственная проблема, которую мне пришлось обойти, заключалась в том, что подписка срабатывала дважды, когда элемент был новым письмом в папке «Входящие», я получил уведомление о событии NewMail и событии Created. Я реализовал обходной путь, который проверяет, что сообщение еще не было зарегистрировано в моей службе прослушивания. Все отлично работало.

Сегодня мы начали тестировать два почтовых ящика, за которыми одновременно наблюдают. Два почтовых ящика были моими и еще одним почтовым ящиком разработчиков. Мы обнаружили самое странное поведение. Моя подписка сработала, как и ожидалось. Но он этого не сделал, входящая часть его подписки работала правильно, но ни одно электронное письмо, которое он отправлял службе прослушивания, никогда не было отправлено уведомлением. Глядя на свойства почтового ящика в Exchange, я не вижу никакой разницы между его почтовым ящиком и моим. Мы даже сравнили параметры/настройки в Outlook. Я не вижу причин, почему это работает на моем почтовом ящике, а не на его.

Есть ли что-то, чего мне не хватает при создании подписки. Я не думал, что это так, поскольку моя подписка работает должным образом.

Мой код службы прослушивания работает отлично. Я разместил код ниже, если кто-то захочет его увидеть, чтобы убедиться, что это не проблема.

Заранее спасибо, Терри

Код службы прослушивания:

/// <summary>
/// Summary description for PushNotificationClient
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
// [System.Web.Script.Services.ScriptService]
public class PushNotificationClient : System.Web.Services.WebService, INotificationServiceBinding
{
  ExchangeService ewService = new ExchangeService(ExchangeVersion.Exchange2007_SP1);

  public PushNotificationClient()
  {
    //todo: init the service.
    SetupExchangeWebService();
  }

  private void SetupExchangeWebService()
  {
    ewService.Credentials = Settings.ServiceCreds;
    try
    {
      ewService.AutodiscoverUrl(Settings.AutoDiscoverThisEmailAddress);
    }
    catch (AutodiscoverRemoteException e)
    {
      //log auto discovery failed
      ewService.Url = Settings.ExchangeService;
    }
  }

  public SendNotificationResultType SendNotification(SendNotificationResponseType SendNotification1)
  {
    using (var _users = new ExchangeUser(Settings.SqlConnectionString))
    {
      var result = new SendNotificationResultType();

      var responseMessages = SendNotification1.ResponseMessages.Items;
      foreach (var responseMessage in responseMessages)
      {
        if (responseMessage.ResponseCode != ResponseCodeType.NoError)
        {
          //log error and unsubscribe.
          result.SubscriptionStatus = SubscriptionStatusType.Unsubscribe;
          return result;
        }

        var sendNoficationResponse = responseMessage as SendNotificationResponseMessageType;
        if (sendNoficationResponse == null)
        {
          result.SubscriptionStatus = SubscriptionStatusType.Unsubscribe;
          return result;
        }

        var notificationType = sendNoficationResponse.Notification;
        var subscriptionId = notificationType.SubscriptionId;
        var previousWatermark = notificationType.PreviousWatermark;

        User user = _users.GetById(subscriptionId);
        if (user != null)
        {
          if (user.MonitorEmailYN == true)
          {
            BaseNotificationEventType[] baseNotifications = notificationType.Items;

            for (int i = 0; i < notificationType.Items.Length; i++)
            {
              if (baseNotifications[i] is BaseObjectChangedEventType)
              {
                var bocet = baseNotifications[i] as BaseObjectChangedEventType;
                AccessCreateDeleteNewMailEvent(bocet, ref user);
              }
            }

            _PreviousItemId = null;
          }
          else
          {
            user.SubscriptionID = String.Empty;
            user.SubscriptionStatusDateTime = null;
            user.Watermark = String.Empty;
            _users.Update(user);

            result.SubscriptionStatus = SubscriptionStatusType.Unsubscribe;
            return result;
          }

          user.SubscriptionStatusDateTime = DateTime.Now.ToLocalTime();
          _users.Update(user);
        }
        else
        {
          result.SubscriptionStatus = SubscriptionStatusType.Unsubscribe;
          return result;
        }
      }

      result.SubscriptionStatus = SubscriptionStatusType.OK;
      return result;
    }
  }

  private string _PreviousItemId;
  private void AccessCreateDeleteNewMailEvent(BaseObjectChangedEventType bocet, ref User user)
  {
    var watermark = bocet.Watermark;
    var timestamp = bocet.TimeStamp.ToLocalTime();
    var parentFolderId = bocet.ParentFolderId;

    if (bocet.Item is ItemIdType)
    {
      var itemId = bocet.Item as ItemIdType;
      if (itemId != null)
      {
        if (string.IsNullOrEmpty(_PreviousItemId) || (!string.IsNullOrEmpty(_PreviousItemId) && _PreviousItemId != itemId.Id))
        {
          ProcessItem(itemId, ref user);
          _PreviousItemId = itemId.Id;
        }
      }
    }

    user.SubscriptionStatusDateTime = timestamp;
    user.Watermark = watermark;
    using (var _users = new ExchangeUser(Settings.SqlConnectionString))
    {
      _users.Update(user);
    }

  }

  private void ProcessItem(ItemIdType itemId, ref User user)
  {
    try
    {
      ewService.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, user.EmailAddress);
      EmailMessage email = EmailMessage.Bind(ewService, itemId.Id);
      using (var _entity = new SalesAssistantEntityDataContext(Settings.SqlConnectionString))
      {
        var direction = EmailDirection.Incoming;
        if (email.From.Address == user.EmailAddress)
        {
          direction = EmailDirection.Outgoing;
        }


        int? bodyType = (int)email.Body.BodyType;

        var _HtmlToRtf = new HtmlToRtf();
        var message = _HtmlToRtf.ConvertHtmlToText(email.Body.Text);

        bool? IsIncoming = Convert.ToBoolean((int)direction);

        if (IsIncoming.HasValue && IsIncoming.Value == false)
        {
          foreach (var emailTo in email.ToRecipients)
          {
            _entity.InsertMailMessage(email.From.Address, emailTo.Address, email.Subject, message, bodyType, IsIncoming);
          }
        }
        else
        {
          if (email.ReceivedBy != null)
          {
            _entity.InsertMailMessage(email.From.Address, email.ReceivedBy.Address, email.Subject, message, bodyType, IsIncoming);
          }
          else
          {
            var emailToFind = user.EmailAddress;
            if (email.ToRecipients.Any(x => x.Address == emailToFind))
            {
              _entity.InsertMailMessage(email.From.Address, emailToFind, email.Subject, message, bodyType, IsIncoming);
            }
          }
        }
      }
    }
    catch(Exception e)
    {
      //Log exception 
      using (var errorHandler = new ErrorHandler(Settings.SqlConnectionString))
      {
        errorHandler.LogException(e, user.UserID, user.SubscriptionID, user.Watermark, user.SubscriptionStatusDateTime);
      }
      throw e;
    }
  }

}

person Terry Nederveld    schedule 24.03.2011    source источник
comment
Хорошо, мы нашли разницу, но как это сделать, мы преодолеем проблему. Оказывается, мой Outlook не использовал режим кэширования, а пара, которую мы тестировали, использовала его. Как только один из них отключил режим кеша, созданное SentItems уведомление попало в систему. Я не понимаю этого и не знаю, как это обойти, так как считаю, что режим кэширования используется по умолчанию при первой установке Outlook. У кого-нибудь есть идеи?   -  person Terry Nederveld    schedule 24.03.2011


Ответы (1)


У меня есть два ответа для вас. Сначала вам нужно будет создать один экземпляр ExchangeService для каждого пользователя. Как я понимаю ваш код, вы просто создаете один экземпляр и переключаете олицетворение, которое не поддерживается. Я разработал WindowsService, который очень похож на ваш. Мой синхронизирует почту между нашей CRM и Exchange. Поэтому при запуске я создаю экземпляр для каждого пользователя и кэширую его, пока приложение работает.

Теперь о кэш-режиме. Разница между использованием режима кэширования и не является просто временной задержкой. В режиме кэширования Outlook время от времени синхронизируется. И без кэширования это вовремя. Когда вы используете режим кеша и хотите, чтобы события немедленно попадали на ваш Exchange-сервер, вы можете нажать кнопку «отправить и получить» в Outlook, чтобы принудительно выполнить синхронизацию.

Надеюсь, это поможет вам...

person Jürgen Hoffmann    schedule 28.03.2013