Несколько подписок PushNotification некоторые работают должным образом, а некоторые нет - PullRequest
6 голосов
/ 24 марта 2011

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

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

  1. SubscriptionId - 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 Server.

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;
    }
  }

}

1 Ответ

2 голосов
/ 28 марта 2013

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

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

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

...