/// <summary> /// Creates pull subscriptions for all rooms and waits on timer delay to pull subscriptions /// </summary> /// <param name="mailboxOwner"></param> /// <returns></returns> private async System.Threading.Tasks.Task PullSubscriptionChangesAsync(string mailboxOwner) { _traceListener.Trace("SyncProgram", $"PullSubscriptionChangesAsync({mailboxOwner}) starting"); var service = new EWService(EwsToken); service.SetImpersonation(ConnectingIdType.SmtpAddress, mailboxOwner); if (_subscriptions == null) { _subscriptions = new List <SubscriptionCollection>(); } var EwsService = new EWService(EwsToken); EwsService.SetImpersonation(ConnectingIdType.SmtpAddress, mailboxOwner); // Retreive and Store PullSubscription Details using (var _context = new EWSDbContext(EWSConstants.Config.Database)) { foreach (var room in _context.RoomListRoomEntities.Where(w => !string.IsNullOrEmpty(w.Identity))) { EntitySubscription dbSubscription = null; string watermark = null; if (_context.SubscriptionEntities.Any(rs => rs.SmtpAddress == room.SmtpAddress)) { dbSubscription = _context.SubscriptionEntities.FirstOrDefault(rs => rs.SmtpAddress == room.SmtpAddress); watermark = dbSubscription.Watermark; } else { // newup a subscription to track the watermark dbSubscription = new EntitySubscription() { LastRunTime = DateTime.UtcNow, SmtpAddress = room.SmtpAddress }; _context.SubscriptionEntities.Add(dbSubscription); } try { var roomService = new EWService(EwsToken); var subscription = roomService.CreatePullSubscription(ConnectingIdType.SmtpAddress, room.SmtpAddress, pollingTimeout, watermark); // close out the old subscription dbSubscription.PreviousWatermark = (!string.IsNullOrEmpty(watermark)) ? watermark : null; dbSubscription.Watermark = subscription.Watermark; _traceListener.Trace("SyncProgram", $"ListenToRoomReservationChangesAsync.Subscribed to room {room.SmtpAddress}"); _subscriptions.Add(new SubscriptionCollection() { Pulling = subscription, SmtpAddress = room.SmtpAddress }); var rowChanged = _context.SaveChanges(); _traceListener.Trace("SyncProgram", $"Pull subscription persisted {rowChanged} rows"); } catch (Microsoft.Exchange.WebServices.Data.ServiceRequestException srex) { _traceListener.Trace("SyncProgram", $"Failed to provision subscription {srex.Message}"); throw new Exception($"Subscription could not be created for {room.SmtpAddress} with MSG:{srex.Message}"); } } } try { var waitTimer = new TimeSpan(0, 5, 0); while (!CancellationTokenSource.IsCancellationRequested) { var milliseconds = (int)waitTimer.TotalMilliseconds; using (var _context = new EWSDbContext(EWSConstants.Config.Database)) { foreach (var item in _subscriptions) { bool?ismore = default(bool); do { PullSubscription subscription = item.Pulling; var events = subscription.GetEvents(); var watermark = subscription.Watermark; ismore = subscription.MoreEventsAvailable; var email = item.SmtpAddress; var databaseItem = _context.SubscriptionEntities.FirstOrDefault(rs => rs.SmtpAddress == email); // pull last event from stack TODO: need heuristic for how meetings can be stored var filteredEvents = events.ItemEvents.OrderBy(x => x.TimeStamp); foreach (ItemEvent ev in filteredEvents) { var itemId = ev.ItemId; try { // Send an item event you can bind to await Messenger.SendQueueO365ChangesAsync(queueSubscription, email, ev); } catch (ServiceResponseException ex) { _traceListener.Trace("SyncProgram", $"ServiceException: {ex.Message}"); continue; } } databaseItem.Watermark = watermark; databaseItem.LastRunTime = DateTime.UtcNow; // Save Database changes await _context.SaveChangesAsync(); }while (ismore == true); } } _traceListener.Trace("SyncProgram", $"Sleeping at {DateTime.UtcNow} for {milliseconds} milliseconds..."); System.Threading.Thread.Sleep(milliseconds); } } catch (Exception ex) { Console.WriteLine(ex.Message); } _traceListener.Trace("SyncProgram", $"PullSubscriptionChangesAsync({mailboxOwner}) exiting"); }
/// <summary> /// Establish the connection /// </summary> /// <returns></returns> async private System.Threading.Tasks.Task RunAsync() { _traceListener = new ClassTraceListener(); IsDisposed = false; EwsToken = await EWSConstants.AcquireTokenAsync(); Messenger = new MessageManager(CancellationTokenSource); ServiceCredentials = new OAuthCredentials(EwsToken.AccessToken); _reconnect = true; _groups = new Dictionary <string, GroupInfo>(); _mailboxes = new Mailboxes(ServiceCredentials, _traceListener); _subscriptions = new List <SubscriptionCollection>(); _connections = new Dictionary <string, StreamingSubscriptionConnection>(); var impersonationId = EWSConstants.Config.Exchange.ImpersonationAcct; try { var list = new List <EWSFolderInfo>(); using (EWSDbContext context = new EWSDbContext(EWSConstants.Config.Database)) { foreach (var room in context.RoomListRoomEntities.Where(w => !string.IsNullOrEmpty(w.Identity))) { var mailboxId = room.SmtpAddress; try { var roomService = new EWService(EwsToken); roomService.SetImpersonation(ConnectingIdType.SmtpAddress, mailboxId); var folderId = new FolderId(WellKnownFolderName.Calendar, mailboxId); var info = new EWSFolderInfo() { SmtpAddress = mailboxId, Service = roomService, Folder = folderId }; list.Add(info); } catch (Exception srex) { _traceListener.Trace("SyncProgram", $"Failed to ProcessChanges{srex.Message}"); throw new Exception($"ProcessChanges for {mailboxId} with MSG:{srex.Message}"); } } } // Parallel ForEach (on RoomMailbox Grouping and SyncFolders) await System.Threading.Tasks.Task.Run(() => { ParallelOptions options = new ParallelOptions { MaxDegreeOfParallelism = maxConcurrency }; // Fireoff folder sync in background thread Parallel.ForEach(list, options, (bodyInfo) => { ProcessChanges(bodyInfo); }); }); var tasks = new List <System.Threading.Tasks.Task>(); if (EWSConstants.Config.Exchange.PullEnabled) { // Upon completion tick repeater to poll subscriptions tasks.Add(PullSubscriptionChangesAsync(impersonationId)); } else { // Upon completion kick of streamingsubscription tasks.Add(CreateStreamingSubscriptionGroupingAsync(impersonationId)); } System.Threading.Tasks.Task.WaitAll(tasks.ToArray()); } catch (Exception ex) { Trace.TraceError(ex.Message); } }