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 HandleCollections(ConnectionBase connection, string contextName, Type dbContextType,
                                      List <ChangeResponse> changes, IServiceProvider requestServiceProvider)
        {
            IEnumerable <IGrouping <string, CollectionSubscription> > subscriptionGroupings = connection.Subscriptions
                                                                                              .Where(s => s.ContextName == contextName)
                                                                                              .GroupBy(s => s.CollectionName);

            foreach (IGrouping <string, CollectionSubscription> subscriptionGrouping in subscriptionGroupings)
            {
                KeyValuePair <Type, string> property = dbContextType.GetDbSetType(subscriptionGrouping.Key);

                List <ChangeResponse> changesForCollection = changes
                                                             .Where(c => c.CollectionName == subscriptionGrouping.Key)
                                                             .ToList();

                AuthModelInfo modelInfo = property.Key.GetAuthModelInfos();

                IEnumerable <ChangeResponse> authenticatedChanges = changesForCollection;

                if (modelInfo.QueryEntryAuthAttributes.Any())
                {
                    authenticatedChanges = changesForCollection
                                           .Where(change => change.State == ChangeResponse.ChangeState.Deleted ||
                                                  property.Key.CanQueryEntry(connection.Information, requestServiceProvider,
                                                                             change.Value));


                    IEnumerable <ChangeResponse> oldLoadedNotAllowed = changesForCollection
                                                                       .Where(change => change.State == ChangeResponse.ChangeState.Modified &&
                                                                              !property.Key.CanQueryEntry(connection.Information, requestServiceProvider,
                                                                                                          change.Value))
                                                                       .Select(change =>
                    {
                        ChangeResponse newChangeResponse = change.CreateResponse(null, change.Value);
                        newChangeResponse.State          = ChangeResponse.ChangeState.Deleted;
                        return(newChangeResponse);
                    });

                    authenticatedChanges = authenticatedChanges.Concat(oldLoadedNotAllowed);
                }

                List <ChangeResponse> collectionChanges = authenticatedChanges.ToList();

                if (collectionChanges.Any())
                {
                    QueryFunctionAttribute queryFunctionAttribute =
                        property.Key.GetCustomAttribute <QueryFunctionAttribute>(false);
                    if (queryFunctionAttribute != null)
                    {
                        var queryFunctionInfo = property.Key.GetMethod(queryFunctionAttribute.Function,
                                                                       BindingFlags.Default | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);

                        if (queryFunctionInfo != null)
                        {
                            object[] methodParameters =
                                queryFunctionInfo.CreateParameters(connection.Information, serviceProvider);
                            dynamic queryFunctionExpression =
                                ((dynamic)queryFunctionInfo.Invoke(null, methodParameters)).Compile();

                            collectionChanges = collectionChanges.Where(change => queryFunctionExpression(change.Value))
                                                .ToList();
                        }
                    }
                }

                foreach (CollectionSubscription subscription in subscriptionGrouping)
                {
                    Task.Run(() =>
                    {
                        HandleSubscription(subscription, dbContextType, requestServiceProvider, connection,
                                           property, collectionChanges, changes);
                    });
                }
            }
        }
        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);
            }
        }