我正在使用实体框架核心的HasQueryFilter方法,同时在此过滤器表达式中包含动态变量。由于存在这个动态参数(我将其命名为“ MinPriority”),因此我无法直接传递lambda表达式,例如
HasQueryFilter(x => x.Priority >= Program.MinPriority);
作为过滤器,它会编译并忽略对MinPriority的任何更改。这就是为什么我会在每个调用中生成一个新的func的原因,
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提供查询过滤器吗?
答案 0 :(得分:3)
绝对有可能。
要求是动态部分必须源自上下文类的实例成员(字段,属性,方法)。
您做了些什么。问题是Func
(以及基本上任何接收实体并返回bool的方法)都无法转换为SQL,并且需要client evaluation,当将其应用于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);
}
}
在Global Query Filters文档中对此进行了解释,但仅作为对example的提示的提示:
请注意DbContext实例级别字段的使用:
_tenantId
用于设置当前租户。模型级过滤器将使用正确上下文实例(即正在执行查询的实例)中的值。
我希望看到的是动态过滤器要求和限制的清晰说明。