exchangewebservices - 多个 PushNotification 订阅有些可以正常工作,有些则不能

标签 exchangewebservices exchange-server-2007 ews-managed-api

我尝试在 Exchange Development 论坛上发帖但没有得到任何回复,所以我会在这里尝试。 Link to forum

我有一个每十五分钟触发一次的 Windows 服务,以查看是否有任何需要创建或更新的订阅。我对 Exchange 2007 SP1 使用托管 API v1.1。我有一个表,其中存储了所有想要监控邮箱的用户。因此,当“监听服务”收到通知时,我可以查找用户并访问消息以将其登录到我们正在构建的应用程序中。在表中,我有以下列存储订阅信息:

  • SubscriptionId - VARCHAR(MAX)
  • 水印 - VARCHAR(MAX)
  • 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);
    }
    

    我们还运行了以下 cmdlet,为我们正在访问 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;
        }
      }
    
    }
    

    最佳答案

    我有两个答案给你。
    首先,您必须为每个用户创建一个 ExchangeService 实例。就像我理解您的代码一样,您只需创建一个实例并切换模拟,这是不受支持的。我开发了一个和你的非常相似的 windowsservice。我的正在同步我们的 CRM 和 Exchange 之间的邮件。所以在启动时,我为每个用户创建一个实例,并在应用程序运行时缓存它。

    现在关于缓存模式。使用缓存模式和不使用缓存模式的区别只是时间问题。在缓存模式下,Outlook 会不时同步。并且非缓存它是及时的。当您使用缓存模式并希望事件立即在您的 Exchange 服务器上时,您可以按 Outlook 中的“发送和接收”按钮强制同步。

    希望能帮到你...

    关于exchangewebservices - 多个 PushNotification 订阅有些可以正常工作,有些则不能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5419497/

    相关文章:

    c# - EWS 托管 API 1.1 - 拉取订阅内存泄漏

    exchange-server-2007 - 以编程方式监视 Exchange 收件箱和打印标签

    Powershell命令从交换地址列表中隐藏用户

    c# - 如何为通过 Exchange Server 发送的邮件设置 MHT-body?

    c# - Exchange 401 未经授权

    c# - 使用 EWS 托管 API 2.0 为现有约会绑定(bind)自定义扩展属性

    c# - 交换 Web 服务错误 - 远程服务器返回错误 405 方法不允许

    c# - EWS 交换有时不会使用 StreamingSubscription 触发 NewMail 事件

    .net - 我们可以使用EWS托管API连接到Exchange 2016吗?

    java - EWS Java API 1.2 - 解析信件正文的最佳方法