public void Build_A_Document_Subscription() { var collectionName = "testCollection"; var id = new BsonValue(Guid.NewGuid()); var collSub = new CollectionSubscription <Model>(_db.NotificationService) { Collection = collectionName, }; var collBuilder = new CollectionSubscriptionBuilder <Model>(_db.NotificationService, collSub); var docBuilder = collBuilder.Id(id) as DocumentSubscriptionBuilder <Model>; var docSub = (docBuilder as ISubscriptionBuilderBase).Subscription as DocumentSubscription <Model>; docSub.Collection.Should().Be(collectionName); docSub.Id.Should().Be(id); // before subscribing docSub.Observer.Should().BeNull(); docBuilder.Subscribe(obj => { }); // after subscribing docSub.Observer.Should().NotBeNull(); }
/// <summary> /// Notify one subscription /// </summary> public void Notify <T>(CollectionSubscription <T> collectionSubscription) where T : class { if (_database is null) { throw new InvalidOperationException("notification service is not initialized"); } var nextValue = _database.GetCollection <T>(collectionSubscription.Collection).Query().ToList(); Task.Run(() => collectionSubscription.Observer?.OnNext(nextValue)); }
internal void NotifyIfNeeded <T>(CollectionSubscription <T> collectionSubscription, NotificationCache cache) where T : class { if (cache.Broadcasts.Contains(collectionSubscription.Collection)) { Notify(collectionSubscription); return; } if (cache.Collections.Contains(collectionSubscription.Collection)) { Notify(collectionSubscription); return; } }
public async Task AddSubscription(CollectionSubscription subscription) { await Lock.WaitAsync(); try { if (Subscriptions.All(s => s.ReferenceId != subscription.ReferenceId)) { Subscriptions.Add(subscription); } } finally { Lock.Release(); } }
public void Unsubscribe_From_DB_When_Disposing() { var subscriptions = new SubscriptionDict(); var sub1 = new DocumentSubscription <Model>(_db.NotificationService); var sub2 = new CollectionSubscription <Model>(_db.NotificationService); subscriptions.TryAdd(sub1, default); subscriptions.TryAdd(sub2, default); subscriptions.Keys.Should().HaveCount(2); subscriptions.Keys.Should().Contain(sub1); subscriptions.Keys.Should().Contain(sub2); using (var unsubscriber = new Unsubscriber(subscriptions, sub1)) { } subscriptions.Keys.Should().HaveCount(1); subscriptions.Keys.Should().Contain(sub2); }
public async Task <ResponseBase> Handle(HttpInformation context, SubscribeCommand command) { ResponseBase response = CollectionHelper.GetCollection(GetContext(command.ContextName), command, context, serviceProvider); if (response.Error == null) { CollectionSubscription collectionSubscription = new CollectionSubscription() { CollectionName = command.CollectionName.ToLowerInvariant(), ContextName = command.ContextName.ToLowerInvariant(), ReferenceId = command.ReferenceId, Prefilters = command.Prefilters }; await Connection.AddSubscription(collectionSubscription); } return(response); }
public void Build_A_Collection_Subscription() { var collectionName = "testCollection"; var id = new BsonValue(Guid.NewGuid()); var sub = new CollectionSubscription <Model>(_db.NotificationService) { Collection = collectionName, }; var builder = new CollectionSubscriptionBuilder <Model>(_db.NotificationService, sub); sub.Collection.Should().Be(collectionName); // before subscribing sub.Observer.Should().BeNull(); builder.Subscribe(listObj => { }); // after subscribing sub.Observer.Should().NotBeNull(); }
private bool HasIncludePrefilterWithChange(CollectionSubscription subscription, List <ChangeResponse> allChanges) { List <IncludePrefilter> includePrefilters = subscription.Prefilters.OfType <IncludePrefilter>().ToList(); if (!includePrefilters.Any()) { return(false); } List <string> affectedCollections = includePrefilters .SelectMany(prefilter => prefilter.AffectedCollectionNames) .Distinct() .ToList(); return(allChanges.Any(change => change.CollectionName.Equals(subscription.CollectionName, StringComparison.InvariantCultureIgnoreCase)) || affectedCollections.Any(collectionName => allChanges.Any(change => change.CollectionName.Equals(collectionName, StringComparison.InvariantCultureIgnoreCase)))); }
public void HandleSubscription(CollectionSubscription subscription, Type dbContextType, IServiceProvider requestServiceProvider, ConnectionBase connection, KeyValuePair <Type, string> property, List <ChangeResponse> collectionChanges, List <ChangeResponse> allChanges) { try { bool anyCollectionChanges = collectionChanges.Any(); if ((anyCollectionChanges && subscription.Prefilters.Any(prefilter => prefilter is IAfterQueryPrefilter || prefilter is TakePrefilter || prefilter is SkipPrefilter)) || HasIncludePrefilterWithChange(subscription, allChanges)) { SapphireDbContext db = dbContextAccessor.GetContext(dbContextType, requestServiceProvider); IQueryable <object> collectionValues = db.GetCollectionValues(requestServiceProvider, connection.Information, property, subscription.Prefilters); IAfterQueryPrefilter afterQueryPrefilter = subscription.Prefilters.OfType <IAfterQueryPrefilter>().FirstOrDefault(); if (afterQueryPrefilter != null) { afterQueryPrefilter.Initialize(property.Key); _ = connection.Send(new QueryResponse() { ReferenceId = subscription.ReferenceId, Result = afterQueryPrefilter.Execute(collectionValues) }); } else { _ = connection.Send(new QueryResponse() { ReferenceId = subscription.ReferenceId, Result = collectionValues .AsEnumerable() .Select(v => v.GetAuthenticatedQueryModel(connection.Information, serviceProvider)) .ToList() }); } } else if (anyCollectionChanges) { IEnumerable <WherePrefilter> wherePrefilters = subscription.Prefilters.OfType <WherePrefilter>(); foreach (WherePrefilter wherePrefilter in wherePrefilters) { wherePrefilter.Initialize(property.Key); Func <object, bool> whereFunction = wherePrefilter.WhereExpression.Compile(); collectionChanges = collectionChanges.Where((change) => whereFunction(change.Value)).ToList(); } collectionChanges.ForEach(change => { object value = change.Value.GetAuthenticatedQueryModel(connection.Information, requestServiceProvider); _ = connection.Send(change.CreateResponse(subscription.ReferenceId, value)); }); } } catch (Exception ex) { SubscribeCommand tempErrorCommand = new SubscribeCommand() { CollectionName = subscription.CollectionName, ReferenceId = subscription.ReferenceId, Prefilters = subscription.Prefilters }; _ = connection.Send(tempErrorCommand.CreateExceptionResponse <ResponseBase>(ex)); logger.LogError( $"Error handling subscription '{subscription.ReferenceId}' of {subscription.CollectionName}"); logger.LogError(ex.Message); } }
public void HandleSubscription(CollectionSubscription subscription, Type dbContextType, IServiceProvider requestServiceProvider, ConnectionBase connection, KeyValuePair <Type, string> property, List <ChangeResponse> collectionChanges, List <ChangeResponse> allChanges) { try { bool anyCollectionChanges = collectionChanges.Any(); if ((anyCollectionChanges && subscription.Prefilters.Any(prefilter => prefilter is IAfterQueryPrefilter || prefilter is TakePrefilter || prefilter is SkipPrefilter)) || HasIncludePrefilterWithChange(subscription, allChanges)) { SapphireDbContext db = dbContextAccessor.GetContext(dbContextType, requestServiceProvider); IQueryable <object> collectionValues = db.GetCollectionValues(requestServiceProvider, connection.Information, property, subscription.Prefilters); IAfterQueryPrefilter afterQueryPrefilter = subscription.Prefilters.OfType <IAfterQueryPrefilter>().FirstOrDefault(); if (afterQueryPrefilter != null) { afterQueryPrefilter.Initialize(property.Key); _ = connection.Send(new QueryResponse() { ReferenceId = subscription.ReferenceId, Result = afterQueryPrefilter.Execute(collectionValues) }); } else { _ = connection.Send(new QueryResponse() { ReferenceId = subscription.ReferenceId, Result = collectionValues .AsEnumerable() .Select(v => v.GetAuthenticatedQueryModel(connection.Information, serviceProvider)) .ToList() }); } } else if (anyCollectionChanges) { IEnumerable <WherePrefilter> wherePrefilters = subscription.Prefilters.OfType <WherePrefilter>(); List <ChangeResponse> oldValuesUnloadResponses = new List <ChangeResponse>(); List <ChangeResponse> newValuesLoadResponses = new List <ChangeResponse>(); foreach (WherePrefilter wherePrefilter in wherePrefilters) { wherePrefilter.Initialize(property.Key); // Values that did change know do match the oldValuesUnloadResponses.AddRange( collectionChanges .Where(change => change.State == ChangeResponse.ChangeState.Modified && !wherePrefilter.WhereExpressionCompiled(change.Value) && wherePrefilter.WhereExpressionCompiled(change.OriginalValue)) .Select(change => { ChangeResponse newChangeResponse = change.CreateResponse(null, change.Value); newChangeResponse.State = ChangeResponse.ChangeState.Deleted; return(newChangeResponse); }) ); newValuesLoadResponses.AddRange( collectionChanges .Where(change => change.State == ChangeResponse.ChangeState.Modified && wherePrefilter.WhereExpressionCompiled(change.Value) && !wherePrefilter.WhereExpressionCompiled(change.OriginalValue)) .Select(change => { ChangeResponse newChangeResponse = change.CreateResponse(null, change.Value); newChangeResponse.State = ChangeResponse.ChangeState.Added; return(newChangeResponse); }) ); collectionChanges = collectionChanges .Where((change) => wherePrefilter.WhereExpressionCompiled(change.Value)).ToList(); } IEnumerable <ChangeResponse> changesForWherePrefilter = oldValuesUnloadResponses .Concat(newValuesLoadResponses) .GroupBy(v => v.Value) .Select(g => g.LastOrDefault()); collectionChanges = collectionChanges.Concat(changesForWherePrefilter).ToList(); ChangesResponse changesResponse = new ChangesResponse() { ReferenceId = subscription.ReferenceId, Changes = collectionChanges.Select(change => { object value = change.Value.GetAuthenticatedQueryModel(connection.Information, requestServiceProvider); return(change.CreateResponse(subscription.ReferenceId, value)); }).ToList() }; if (changesResponse.Changes.Any()) { _ = connection.Send(changesResponse); } } } catch (Exception ex) { SubscribeCommand tempErrorCommand = new SubscribeCommand() { CollectionName = subscription.CollectionName, ReferenceId = subscription.ReferenceId, Prefilters = subscription.Prefilters }; _ = connection.Send(tempErrorCommand.CreateExceptionResponse <ResponseBase>(ex)); logger.LogError( $"Error handling subscription '{subscription.ReferenceId}' of {subscription.CollectionName}"); logger.LogError(ex.Message); } }