Множество абонаменти за PushNotification, някои работят правилно, а други не

Опитах се да публикувам това във форума за разработка на Exchange и не получих никакви отговори, така че ще опитам тук. Връзка към форум

Имам услуга на 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;
    }
  }

}

person Terry Nederveld    schedule 24.03.2011    source източник
comment
Добре, открихме разликата, но как да преодолеем проблема. Оказва се, че моят Outlook не използва Cache Mode и двойката, която тествахме, беше. След като накарах един от тях да изключи режима на кеша, известието, създадено от 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