Динамическое выражение фильтра запросов в include - PullRequest
0 голосов
/ 16 октября 2018

Я работаю с методом ядра структуры сущностей HasQueryFilter, имея динамическую переменную в этом выражении фильтра.Из-за этого динамического параметра (давайте назовем его «MinPriority») я не могу напрямую передать лямбда-выражение типа

HasQueryFilter(x => x.Priority >= Program.MinPriority);

в качестве фильтра, так как он компилирует и игнорирует любые изменения в MinPriority.Вот почему я генерирую новую функцию при каждом вызове, например:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<GroupItem>().HasQueryFilter(x => GenerateMyFilter()(x));
}

protected Func<GroupItem, bool> GenerateMyFilter()
{
    return x => x.Priority >= Program.MinPriority;
}

, это прекрасно работает для

dbContext.GroupItems.ToList()

я также могу изменить MinPriority, а изменение этой переменной регионализируется с помощьюфильтр.

, но при вызове этой сущности включением исключение генерируется ядром EF:

dbContext.Groups.Include(x => x.Items).ToList()

выдает исключение NullReferenceException:

   at lambda_method(Closure , AnonymousObject )
   at System.Linq.Lookup`2.CreateForJoin(IEnumerable`1 source, Func`2 keySelector, IEqualityComparer`1 comparer)
   at System.Linq.Enumerable.JoinIterator[TOuter,TInner,TKey,TResult](IEnumerable`1 outer, IEnumerable`1 inner, Func`2 outerKeySelector, Func`2 innerKeySelector, Func`3 resultSelector, IEqualityComparer`1 comparer)+MoveNext()
   at System.Collections.Generic.EnumerableHelpers.ToArray[T](IEnumerable`1 source, Int32& length)
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   at System.Linq.OrderedEnumerable`1.GetEnumerator()+MoveNext()
   at System.Linq.Enumerable.SelectIPartitionIterator`2.MoveNext()
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryBuffer.IncludeCollection[TEntity,TRelated,TElement](Int32 includeId, INavigation navigation, INavigation inverseNavigation, IEntityType targetEntityType, IClrCollectionAccessor clrCollectionAccessor, IClrPropertySetter inverseClrPropertySetter, Boolean tracking, TEntity entity, Func`1 relatedEntitiesFactory, Func`3 joinPredicate)
   at lambda_method(Closure , QueryContext , Group , Object[] )
   at Microsoft.EntityFrameworkCore.Query.Internal.IncludeCompiler._Include[TEntity](QueryContext queryContext, TEntity entity, Object[] included, Action`3 fixup)
   at lambda_method(Closure , Group )
   at System.Linq.Enumerable.SelectEnumerableIterator`2.MoveNext()
   at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider._TrackEntities[TOut,TIn](IEnumerable`1 results, QueryContext queryContext, IList`1 entityTrackingInfos, IList`1 entityAccessors)+MoveNext()
   at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext()
   at System.Collections.Generic.List`1.AddEnumerable(IEnumerable`1 enumerable)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at EFCoreTest.Program.PrintGroups[TDbContext]()

я создал простоеконсольное приложение, которое выдает это исключение: https://gist.github.com/cyptus/f9ac8bb74b2a7d98d148326502600d40

Есть ли другой способ предоставить фильтр запросов для ядра EF DbSet с динамической переменной?

1 Ответ

0 голосов
/ 17 октября 2018

Это определенно возможно.

Требуется, чтобы динамическая часть происходила из члена экземпляра (поля, свойства, метода) класса контекста.

Что вы и сделали.Проблема в том, что Func (и в основном любой метод, получающий сущность и возвращающий bool) не может быть переведен в SQL и требует оценка клиента , которая в настоящее время, по-видимому, не работает при применении к Include.

Даже если это работает, всегда лучше использовать переводимые выражения SQL (оценка сервера).В вашем примере будет достаточно предоставить экземпляр экземпляра контекста для статического Program.MinPriority и использовать его внутри определения глобального фильтра:

public class DynamicQueryDbContext : AppDbContext
{
    protected int MinPriority => Program.MinPriority;

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<GroupItem>()
            .HasQueryFilter(x => x.Priority >= MinPriority);
    }
}

Это как-то объяснено в Глобальные фильтры запросов документация, но только как Подсказка к охватывающему примеру :

Обратите внимание на использование поля уровня экземпляра DbContext: _tenantId используется для установкитекущий арендатор.Фильтры уровня модели будут использовать значение из правильного экземпляра контекста (то есть экземпляра, выполняющего запрос).

То, что я хочу видеть, - это четкое объяснение требований динамического фильтра иограничения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...