我正在对项目进行一些重构,并且回到了以前从未解决过的问题。我正在尝试对EF Core数据库的查询执行多个过滤器。
过去,我曾尝试设置一系列Where语句,这些语句通过匹配过滤器来检查filter语句是否为空或。
这在查询中的某个地方返回了nullReferenceException。我通过不带过滤器的查询运行查询,然后将过滤器应用于列表,解决了该问题。
我回过头来创建了WhereIf扩展,希望它可以解决我的问题,同时使代码更简洁一些,但是会弹出相同的问题。
我目前有四个要在查询中运行的过滤器,它可以通过初始过滤器,但如果选择了其他三个过滤器中的任何一个,则查询将具有nullReferenceException。
如果我从常规查询和第一个过滤器中获得一个列表,然后将这些过滤器应用于我的列表,这将再次起作用。
这就是我想要做的:
IQueryable<Film> films = _context.Films
.Include(f => f.Media)
.Include(f=> f.Audio)
.Include(f => f.FilmGenres)
.ThenInclude(fg => fg.Genre)
.WhereIf(!string.IsNullOrEmpty(vm.SearchValue), f => f.Name.ToLower().Contains(vm.SearchValue.ToLower()))
.WhereIf(!string.IsNullOrEmpty(vm.MediaFilter), f => f.Media.Name == vm.MediaFilter)
.WhereIf(!string.IsNullOrEmpty(vm.AudioFilter), f => f.Audio.Name == vm.AudioFilter)
.WhereIf(!string.IsNullOrEmpty(vm.GenreFilter), f => f.FilmGenres.Any(fg => fg.Genre != null && fg.Genre.Name == vm.GenreFilter));
这是WhereIf方法:
public static IQueryable<TSource> WhereIf<TSource>(this IQueryable<TSource> source, bool condition, Expression<Func<TSource, bool>> predicate)
{
// Performs a Where only when the condition is met
if (condition)
{
source = source.Where(predicate);
return source;
}
return source;
}
vm.SearchValue上的过滤器运行正常,当我逐步执行该过滤器时,该值是预期的IQueryable。一旦它到达其他过滤器中的任何一个,它就会返回nullReferenceException(稍后它最终到达ToList()时)。如果我在返回之前查看source的值,则表明结果视图中具有null异常。
我已经尝试了每一行的内容(电影= film.Where(...))。我试图跳过WhereIf并只执行if语句和标准Where,所有这些都具有相同的结果。
仅当我创建一个List对象时,该对象由数据的常规查询填充,然后过滤该List对象,我才能使用它。
那么,在EF Core中对IQueryable进行过滤有什么问题?这是不允许的,还是我做错了什么?
更新:所有的Film对象确实具有Media / Audio / FilmGenre对象,并且所有内容都包括在内。而且我已经验证了IQueryable源中的项目在WhereIf方法中的Where语句之前具有所有这些项目。
我尝试过将每个过滤器语句分开,这包括跳过WhereIf方法和使用if语句。
此外,一次只能选择一个过滤器(目前)。那些未被选择的结果将导致条件为假,这没有问题。只有在使用有源滤波器时才会打ic。例如,我将进行一次初始搜索,仅检查vm.SearchValue。这将给我列出电影的列表以及进行过滤和排序的选项。然后,当我选择按“音频”或“媒体”等进行过滤时,出现了问题。
这是堆栈跟踪:
at lambda_method(Closure , InternalEntityEntry )
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.SimpleNonNullableDependentKeyValueFactory`1.TryCreateFromCurrentValues(InternalEntityEntry entry, TKey& key)
at Microsoft.EntityFrameworkCore.Query.Internal.WeakReferenceIdentityMap`1.CreateIncludeKeyComparer(INavigation navigation, InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryBuffer.IncludeCore(Object entity, INavigation navigation)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryBuffer.Include(QueryContext queryContext, Object entity, IReadOnlyList`1 navigationPath, IReadOnlyList`1 relatedEntitiesLoaders, Int32 currentNavigationIndex, Boolean queryStateManager)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryBuffer.Include(QueryContext queryContext, Object entity, IReadOnlyList`1 navigationPath, IReadOnlyList`1 relatedEntitiesLoaders, Boolean queryStateManager)
at Microsoft.EntityFrameworkCore.Query.Internal.GroupJoinInclude.GroupJoinIncludeContext.Include(Object entity)
at Microsoft.EntityFrameworkCore.Query.Internal.GroupJoinInclude.GroupJoinIncludeContext.Include(Object entity)
at Microsoft.EntityFrameworkCore.Query.Internal.GroupJoinInclude.GroupJoinIncludeContext.Include(Object entity)
at Microsoft.EntityFrameworkCore.Query.Internal.GroupJoinInclude.GroupJoinIncludeContext.Include(Object entity)
at Microsoft.EntityFrameworkCore.Query.QueryMethodProvider.<_GroupJoin>d__26`4.MoveNext()
at System.Linq.Enumerable.<SelectManyIterator>d__165`3.MoveNext()
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.<_TrackEntities>d__15`2.MoveNext()
at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext()
at System.Collections.Generic.EnumerableHelpers.ToArray[T](IEnumerable`1 source, Int32& length)
at System.Collections.Generic.EnumerableHelpers.ToArray[T](IEnumerable`1 source)
at System.Linq.SystemCore_EnumerableDebugView`1.get_Items()
下面的图像:
更新:此问题已解决。应用程序用户进行了另一项检查,该检查正在引起客户端评估,该检查已被移动,现在查询可以按预期工作。
答案 0 :(得分:0)
我总是只使用一个简单的OR运算符而不是WhereIf
IQueryable<Film> films = _context.Films
.Include(x => x.Media)
.Include(x => x.Audio)
.Include(x => x.FilmGenres)
.ThenInclude(g => g.Genre)
.Where(f => string.IsNullOrEmpty(vm.SearchValue) || f.Name.ToLower().Contains(vm.SearchValue.ToLower()))
.Where(f => string.IsNullOrEmpty(vm.MediaFilter) || f.Media.Name == vm.MediaFilter)
.Where(f => string.IsNullOrEmpty(vm.AudioFilter) || f.Audio.Name == vm.AudioFilter)
.Where(f => string.IsNullOrEmpty(vm.GenreFilter) || (f.FilmGenres.Any(fg => fg.Genre != null && fg.Genre.Name == vm.GenreFilter)));
答案 1 :(得分:0)
此答案已不存在,对我来说有点猜测,因此,如果没有帮助,我深表歉意。
无论如何,有几件事对我很重要。
首先,您的WhereIf()函数-它并没有完全执行Where()会做的事情。 where()获取一个源,并返回第二个源,记录集将在该第二个源中向下传播。值得注意的是,它根本不会更改原始数据源。好吧,您的WhereIf()试图做到这一点-它正在更改传递给函数的'source'变量。我进行了一些谷歌搜索,但IQueryable 并非看起来是不可变的,这意味着可以在不创建新类实例的情况下对其进行更改,因此,我对这行代码没有拧入建立基础的基础是:
source = source.Where(predicate);
...它将解释您得到的结果。第一个具有真实条件的“ WhereIf”有效,但后一个无效-因为第一个与正在处理的基础对象混淆。至少,您应该将其更改为“ return source.Where(predicate)”,仅出于代码清晰起见(因为您现有的代码使它看起来像它的 try 进行更改。)
第二,您是否尝试过将语句分解?我的意思是,像这样:
var results = SomeLinq.SomeStatement(a => something(a))
.Where(b => b == something)
.Where(c => c == something)
...与以下内容相同:
var mainQueryable = SomeLinq.SomeStatement(a => something(a));
var filtered = mainQueryable.Where(b => b == something);
var results = filtered.Where(c => c == something);
反过来又可以让您简化LINQ的图片:
IQueryable<Film> films = _context.Films
.Include(f => f.Media)
.Include(f=> f.Audio)
.Include(f => f.FilmGenres)
.ThenInclude(fg => fg.Genre);
if (!string.IsNullOrEmpty(vm.SearchValue)) films = films.Where(f => f.Equals(vm.SearchValue, StringComparison.OrdinalIgnoreCase);
if (!string.IsNullOrEmpty(vm.MediaFilter)) films = films.Where(f => f.Media.Name == vm.MediaFilter);
// etc...
...因此最终的LINQ语句没有多余的WHERE子句,这些子句实际上并没有过滤掉任何内容。
无论如何,希望这些能有所帮助。