简体   繁体   English

include中的动态查询过滤器表达式

[英]Dynamic query filter expression in include

i am working with the entity framework core HasQueryFilter method while having a dynamic variable within this filter expression. 我正在使用实体框架核心HasQueryFilter方法,同时在此过滤器表达式中有一个动态变量。 Because of this dynamic parameter (lets name it "MinPriority") i cannot directly pass a lambda expression like 由于这个动态参数(让我们将其命名为“MinPriority”),我无法直接传递lambda表达式

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

as filter, as it compiles and ignores any changes to MinPriority. 作为过滤器,因为它编译并忽略对MinPriority的任何更改。 Thats why I generate a new func on every call like: 这就是为什么我在每次调用时生成一个新的函数,如:

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

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

this is working fine for 这个工作正常

dbContext.GroupItems.ToList()

i can also change the MinPriority and the change to this variable is regionized by the filter. 我也可以更改MinPriority,并且过滤器会区分对此变量的更改。

but when calling this entity as an include an exception is thrown by EF core: 但是当调用此实体作为包含时,EF核心会抛出异常:

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

throws NullReferenceException: 抛出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]()

i created a simple console application which produces this exception: https://gist.github.com/cyptus/f9ac8bb74b2a7d98d148326502600d40 我创建了一个简单的控制台应用程序,它产生了这个例外: https//gist.github.com/cyptus/f9ac8bb74b2a7d98d148326502600d40

is there any other way to provide a query filter to an EF core DbSet with an dynamic variable? 有没有其他方法来提供具有动态变量的EF核心DbSet的查询过滤器?

It's definitely possible. 这绝对是可能的。

The requirement is that the dynamic part must originate from instance member (field, property, method) of the context class. 要求是动态部分必须源自上下文类的实例成员(字段,属性,方法)。

Which you sort of did. 你有点做过。 The problem is that the Func (and basically any method receiving an entity and returning bool) cannot be translated to SQL and requires client evaluation , which apparently currently doesn't work when applied to Include . 问题是Func (基本上任何接收实体和返回bool的方法)都无法转换为SQL并且需要客户端评估 ,当应用于Include时显然当前不起作用。

Even if it worked, it's always better to use SQL translatable (server evaluation) expressions. 即使它工作,使用SQL可翻译(服务器评估)表达式总是更好。 In your example it will be sufficient to provide context instance accessor to the static Program.MinPriority and use it inside the global filter definition: 在您的示例中,为静态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);
    }
}

It's sort of explained in the Global Query Filters documentation, but only as a Tip to the encompassing example : 它在全局查询过滤器文档中有所解释,但仅作为包含示例提示

Note the use of a DbContext instance level field: _tenantId used to set the current tenant. 请注意使用DbContext实例级别字段: _tenantId用于设置当前租户。 Model-level filters will use the value from the correct context instance (that is, the instance that is executing the query). 模型级过滤器将使用来自正确上下文实例的值(即,正在执行查询的实例)。

What I wish to see there is clear explanation of the dynamic filter requirements and limitations. 我希望看到有关动态过滤器要求和限制的明确解释。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM