コード例 #1
0
        /// <summary>
        /// A more flexible filtering method than Filter(). Filter() will always return materialized items.
        /// </summary>
        public IEnumerable <TEntityInterface> FilterOrQuery(IEnumerable <TEntityInterface> items, object parameter, Type parameterType)
        {
            bool preferQuery = items is IQueryable;

            // If exists use Filter(IQueryable, TParameter) or Filter(IEnumerable, TParameter)
            {
                ReadingOption enumerableFilter = () =>
                {
                    var reader = Reflection.RepositoryEnumerableFilterMethod(parameterType);
                    if (reader == null)
                    {
                        return(null);
                    }
                    return(() =>
                    {
                        _logger.Trace(() => "Filtering using enumerable Filter(items, " + reader.GetParameters()[1].ParameterType.FullName + ")");
                        Reflection.MaterializeEntityList(ref items);
                        return (IEnumerable <TEntityInterface>)reader.InvokeEx(_repository.Value, items, parameter);
                    });
                };

                ReadingOption queryableFilter = () =>
                {
                    var reader = Reflection.RepositoryQueryableFilterMethod(parameterType);
                    if (reader == null)
                    {
                        return(null);
                    }
                    return(() =>
                    {
                        _logger.Trace(() => "Filtering using queryable Filter(items, " + reader.GetParameters()[1].ParameterType.FullName + ")");
                        var query = Reflection.AsQueryable(items);
                        return (IEnumerable <TEntityInterface>)reader.InvokeEx(_repository.Value, query, parameter);
                    });
                };

                ReadingOptions options;
                if (!preferQuery)
                {
                    options = new ReadingOptions {
                        enumerableFilter, queryableFilter
                    }
                }
                ;
                else
                {
                    options = new ReadingOptions {
                        queryableFilter, enumerableFilter
                    }
                };

                var readingMethod = options.FirstOptionOrNull();
                if (readingMethod != null)
                {
                    return(readingMethod());
                }
            }

            // If the parameter is FilterAll, unless explicitly implemented above, return all
            if (typeof(FilterAll).IsAssignableFrom(parameterType))
            {
                _logger.Trace(() => "Filtering all items returned.");
                return(items);
            }

            // If the parameter is a generic filter, unless explicitly implemented above, execute it
            if (parameterType == typeof(FilterCriteria))
            {
                _logger.Trace(() => "Filtering using generic filter");
                return(ExecuteGenericFilter(new[] { (FilterCriteria)parameter }, preferQuery, items));
            }
            if (typeof(IEnumerable <FilterCriteria>).IsAssignableFrom(parameterType))
            {
                _logger.Trace(() => "Filtering using generic filters");
                return(ExecuteGenericFilter((IEnumerable <FilterCriteria>)parameter, preferQuery, items));
            }

            // If the parameter is a generic property filter, unless explicitly implemented above, use queryable items.Where(property filter)
            if (typeof(IEnumerable <PropertyFilter>).IsAssignableFrom(parameterType))
            {
                _logger.Trace(() => "Reading using items.AsQueryable().Where(property filter");

                // The filterExpression must use EntityType or EntityNavigationType, depending on the provided query.
                var itemType = items.GetType().GetInterface("IEnumerable`1").GetGenericArguments()[0];

                var filterExpression = _genericFilterHelper.ToExpression((IEnumerable <PropertyFilter>)parameter, itemType);
                if (Reflection.IsQueryable(items))
                {
                    var query = Reflection.AsQueryable(items);
                    return(Reflection.Where(query, filterExpression));
                }
                else
                {
                    return(Reflection.Where(items, filterExpression.Compile()));
                }
            }

            // If the parameter is a filter expression, unless explicitly implemented above, use queryable items.Where(parameter)
            if (Reflection.IsPredicateExpression(parameterType))
            {
                _logger.Trace(() => "Filtering using items.AsQueryable().Where(" + parameterType.Name + ")");
                var query = Reflection.AsQueryable(items);
                return(Reflection.Where(query, (Expression)parameter));
            }

            // If the parameter is a filter function, unless explicitly implemented above, use materialized items.Where(parameter)
            if (typeof(Func <TEntityInterface, bool>).IsAssignableFrom(parameterType))
            {
                _logger.Trace(() => "Filtering using items.Where(" + parameterType.Name + ")");
                var filterFunction = parameter as Func <TEntityInterface, bool>;
                Reflection.MaterializeEntityList(ref items);
                if (filterFunction != null)
                {
                    return(items.Where(filterFunction));
                }
            }

            // If the parameter is a IEnumarable<Guid>, it will be interpreted as filter by IDs.
            if (typeof(IEnumerable <Guid>).IsAssignableFrom(parameterType))
            {
                _logger.Trace(() => "Filtering using items.Where(item => guids.Contains(item.ID))");
                if (!(parameter is List <Guid>))
                {
                    parameter = ((IEnumerable <Guid>)parameter).ToList();
                }

                if (items is IQueryable <TEntityInterface> ) // Use queryable Where function with bool expression instead of bool function.
                {
                    // The query is built by reflection to avoid an obscure problem with complex query in NHibernate:
                    // using generic parameter TEntityInterface or IEntity for a query parameter fails with exception on some complex scenarios.
                    var filterPredicateParameter = Expression.Parameter(Reflection.EntityType, "item");
                    var filterPredicate          = Expression.Lambda(
                        Expression.Call(
                            Expression.Constant(parameter),
                            typeof(List <Guid>).GetMethod("Contains"),
                            new[] { Expression.Property(filterPredicateParameter, "ID") }),
                        filterPredicateParameter);

                    return(Reflection.Where((IQueryable <TEntityInterface>)items, EFExpression.OptimizeContains(filterPredicate)));
                }

                return(items.Where(item => ((List <Guid>)parameter).Contains(item.ID)));
            }

            string errorMessage = $"{EntityName} does not implement a filter with parameter {parameterType.FullName}.";

            if (Reflection.RepositoryLoadWithParameterMethod(parameterType) != null)
            {
                errorMessage += " There is a loader method with this parameter implemented: Try reordering filters to use the " + parameterType.Name + " first.";
                throw new ClientException(errorMessage);
            }
            else
            {
                throw new FrameworkException(errorMessage);
            }
        }
コード例 #2
0
        public IEnumerable <TEntityInterface> Read(object parameter, Type parameterType, bool preferQuery)
        {
            // Use Load(parameter), Query(parameter) or Filter(Query(), parameter), if any of the options exist.
            ReadingOption loaderWithParameter = () =>
            {
                var reader = Reflection.RepositoryLoadWithParameterMethod(parameterType);
                if (reader == null)
                {
                    return(null);
                }
                return(() =>
                {
                    _logger.Trace(() => "Reading using Load(" + reader.GetParameters()[0].ParameterType.FullName + ")");
                    return (IEnumerable <TEntityInterface>)reader.InvokeEx(_repository.Value, parameter);
                });
            };

            ReadingOption queryWithParameter = () =>
            {
                var reader = Reflection.RepositoryQueryWithParameterMethod(parameterType);
                if (reader == null)
                {
                    return(null);
                }
                return(() =>
                {
                    _logger.Trace(() => "Reading using Query(" + reader.GetParameters()[0].ParameterType.FullName + ")");
                    return (IEnumerable <TEntityInterface>)reader.InvokeEx(_repository.Value, parameter);
                });
            };

            ReadingOption queryThenQueryableFilter = () =>
            {
                if (Reflection.RepositoryQueryMethod == null)
                {
                    return(null);
                }
                var reader = Reflection.RepositoryQueryableFilterMethod(parameterType);
                if (reader == null)
                {
                    return(null);
                }
                return(() =>
                {
                    _logger.Trace(() => "Reading using queryable Filter(Query(), " + reader.GetParameters()[1].ParameterType.FullName + ")");
                    var query = Reflection.RepositoryQueryMethod.InvokeEx(_repository.Value);
                    return (IEnumerable <TEntityInterface>)reader.InvokeEx(_repository.Value, query, parameter);
                });
            };

            ReadingOption queryAll = () =>
            {
                if (!typeof(FilterAll).IsAssignableFrom(parameterType))
                {
                    return(null);
                }
                var reader = Reflection.RepositoryQueryMethod;
                if (reader == null)
                {
                    return(null);
                }
                return(() =>
                {
                    _logger.Trace(() => "Reading using Query()");
                    return (IEnumerable <TEntityInterface>)reader.InvokeEx(_repository.Value);
                });
            };

            {
                ReadingOptions options;
                if (!preferQuery)
                {
                    options = new ReadingOptions {
                        loaderWithParameter, queryWithParameter, queryThenQueryableFilter
                    }
                }
                ;
                else
                {
                    options = new ReadingOptions {
                        queryWithParameter, queryThenQueryableFilter, queryAll, loaderWithParameter
                    }
                };

                var readingMethod = options.FirstOptionOrNull();
                if (readingMethod != null)
                {
                    return(readingMethod());
                }
            }

            // If the parameter is FilterAll, unless explicitly implemented above, use Load() or Query() if any option exists
            if (typeof(FilterAll).IsAssignableFrom(parameterType))
            {
                var options = new ReadingOptions {
                    () => {
                        var reader = Reflection.RepositoryLoadMethod;
                        if (reader == null)
                        {
                            return(null);
                        }
                        return(() => {
                            _logger.Trace(() => "Reading using Load()");
                            return (IEnumerable <TEntityInterface>)reader.InvokeEx(_repository.Value);
                        });
                    },
                    () => {
                        var reader = Reflection.RepositoryQueryMethod;
                        if (reader == null)
                        {
                            return(null);
                        }
                        return(() => {
                            _logger.Trace(() => "Reading using Query()");
                            return (IEnumerable <TEntityInterface>)reader.InvokeEx(_repository.Value);
                        });
                    }
                };

                if (preferQuery)
                {
                    options.Reverse();
                }

                var readingMethod = options.FirstOptionOrNull();
                if (readingMethod != null)
                {
                    return(readingMethod());
                }
            }

            // If the parameter is a generic filter, unless explicitly implemented above, execute it
            if (parameterType == typeof(FilterCriteria))
            {
                _logger.Trace(() => "Reading using generic filter");
                return(ExecuteGenericFilter(new[] { (FilterCriteria)parameter }, preferQuery));
            }
            if (typeof(IEnumerable <FilterCriteria>).IsAssignableFrom(parameterType))
            {
                _logger.Trace(() => "Reading using generic filters");
                return(ExecuteGenericFilter((IEnumerable <FilterCriteria>)parameter, preferQuery));
            }

            // If the parameter is a generic property filter, unless explicitly implemented above, use Query().Where(property filter)
            if (Reflection.RepositoryQueryMethod != null && typeof(IEnumerable <PropertyFilter>).IsAssignableFrom(parameterType))
            {
                _logger.Trace(() => "Reading using Query().Where(property filter");
                var query            = (IQueryable <TEntityInterface>)Reflection.RepositoryQueryMethod.InvokeEx(_repository.Value);
                var filterExpression = _genericFilterHelper.ToExpression((IEnumerable <PropertyFilter>)parameter, Reflection.EntityNavigationType);
                return(Reflection.Where(query, filterExpression));
            }

            // If the parameter is a filter expression, unless explicitly implemented above, use Query().Where(parameter)
            if (Reflection.RepositoryQueryMethod != null && Reflection.IsPredicateExpression(parameterType))
            {
                _logger.Trace(() => "Reading using Query().Where(" + parameterType.Name + ")");
                var query = (IQueryable <TEntityInterface>)Reflection.RepositoryQueryMethod.InvokeEx(_repository.Value);
                return(Reflection.Where(query, (Expression)parameter));
            }

            // If the parameter is a IEnumarable<Guid>, it will be interpreted as filter by IDs.
            if (Reflection.RepositoryQueryMethod != null && typeof(IEnumerable <Guid>).IsAssignableFrom(parameterType))
            {
                _logger.Trace(() => "Reading using Query().Where(item => guids.Contains(item.ID))");
                if (!(parameter is List <Guid>))
                {
                    parameter = ((IEnumerable <Guid>)parameter).ToList();
                }
                var query = (IQueryable <TEntityInterface>)Reflection.RepositoryQueryMethod.InvokeEx(_repository.Value);

                // The query is built by reflection to avoid an obscure problem with complex query in NHibernate:
                // using generic parameter TEntityInterface or IEntity for a query parameter fails with exception on some complex scenarios.
                var filterPredicateParameter = Expression.Parameter(Reflection.EntityType, "item");
                var filterPredicate          = Expression.Lambda(
                    Expression.Call(
                        Expression.Constant(parameter),
                        typeof(List <Guid>).GetMethod("Contains"),
                        new[] { Expression.Property(filterPredicateParameter, "ID") }),
                    filterPredicateParameter);

                return(Reflection.Where(query, EFExpression.OptimizeContains(filterPredicate)));
            }

            // It there is only enumerable filter available, use inefficient loader with in-memory filtering: Filter(Load(), parameter)
            {
                var reader = Reflection.RepositoryEnumerableFilterMethod(parameterType);
                if (reader != null)
                {
                    IEnumerable <TEntityInterface> items;
                    try
                    {
                        items = Read(null, typeof(FilterAll), preferQuery: false);
                    }
                    catch (FrameworkException)
                    {
                        items = null;
                    }

                    if (items != null)
                    {
                        _logger.Trace(() => "Reading using enumerable Filter(all, " + reader.GetParameters()[1].ParameterType.FullName + ")");
                        Reflection.MaterializeEntityList(ref items);
                        return((IEnumerable <TEntityInterface>)reader.InvokeEx(_repository.Value, items, parameter));
                    }
                }
            }

            throw new FrameworkException($"{EntityName} does not implement a loader, a query or a filter with parameter {parameterType.FullName}.");
        }