EF:包含where子句

由于标题build议我正在寻找一种方法来做一个where子句结合一个包含。

这是我的情况:我负责支持一个充满代码气味的大型应用程序。 改变太多的代码导致错误到处都是,所以我正在寻找最安全的解决scheme。

比方说,我有一个对象总线和一个对象人(公共汽车有一个导航收集的人)。 在我的查询中,我需要select所有的巴士只有乘客醒着。 这是一个简单的虚拟示例

在当前的代码中:

var busses = Context.Busses.Where(b=>b.IsDriving == true); foreach(var bus in busses) { var passengers = Context.People.Where(p=>p.BusId == bus.Id && p.Awake == true); foreach(var person in passengers) { bus.Passengers.Add(person); } } 

在此代码之后,处理Context,并在调用方法中将所得到的Bus实体映射到DTO类(实体的100%副本)。

此代码会导致多次调用数据库,这是一个不行,所以我在MSDN博客上find了这个解决scheme

debugging结果,但当实体映射到DTO(使用AutoMapper)时,这工作得很好我得到一个exception,上下文/连接已closures,该对象无法加载。 (上下文总是closures不能改变这个:()

所以我需要确保选定的乘客已经加载(IsLoaded导航属性也是假的)。 如果我检查乘客集合计数也抛出exception,但也有Passegers集合称为“包裹的相关实体”,其中包含我的过滤对象的集合。

有没有办法将这些包装的相关实体加载到整个集合中? (我不能更改automapper映射configuration,因为这是在整个应用程序中使用)。

有没有另外一种方法来获得积极的乘客?

任何暗示都欢迎…

编辑

Gert Arnold的答案因为数据没有被加载而不起作用。 但是,当我简化它,并删除它被加载的地方。 这是非常奇怪的,因为execute sql在两种情况下返回所有的乘客。 所以在将结果返回到实体时必定存在问题。

 Context.Configuration.LazyLoadingEnabled = false; var buses = Context.Busses.Where(b => b.IsDriving) .Select(b => new { b, Passengers = b.Passengers }) .ToList() .Select(x => xb) .ToList(); 

EDIT2

经过很多艰苦的工作,格特·阿诺德的答案! 正如Gert Arnoldbuild议您需要禁用延迟加载并closures它。 由于prev开发者喜欢懒惰加载,所以会要求对应用程序进行一些额外的更改-_-

您可以通过查询所需的对象

 Context.Configuration.LazyLoadingEnabled = false; // Or: Context.Configuration.ProxyCreationEnabled = false; var buses = Context.Busses.Where(b => b.IsDriving) .Select(b => new { b, Passengers = b.Passengers .Where(p => p.Awake) }) .AsEnumerable() .Select(x => xb) .ToList(); 

这里发生的事情是,你首先要驾驶巴士,并从数据库中唤醒乘客。 然后, AsEnumerable()从LINQ切换到实体到LINQ到对象,这意味着公交车和乘客将被物化,然后在内存中处理。 这一点很重要,因为如果没有它,EF只会实现最终的预测, Select(x => xb) ,而不是乘客。

现在,EF具有此function关系修正 ,负责设置在上下文中物化的对象之间的所有关联。 这意味着,现在每辆Bus只有其清醒的乘客被装载。

当您通过ToList获取巴士时,您可以使用您想要的乘客的巴士,并且可以使用AutoMapper对其进行映射。

这只适用于懒惰加载被禁用。 否则,在转换到DTO期间,当乘客被访问时,EF将懒惰地装载每辆公共汽车的所有乘客。

有两种方法可以禁用延迟加载。 禁用LazyLoadingEnabled将在再次启用时重新激活延迟加载。 禁用ProxyCreationEnabled将创build不能延迟加载的实体,因此在ProxyCreationEnabled再次启用后,它们将不会启动延迟加载。 当上下文比这个单个查询长时,这可能是最好的select。

但是…多对多

如上所述,这种解决方法依赖于关系修复。 但是,正如Slauma 在这里所解释的,关系修正不适用于多对多关联。 如果BusPassenger是多对多的,唯一可以做的就是自己修理:

 Context.Configuration.LazyLoadingEnabled = false; // Or: Context.Configuration.ProxyCreationEnabled = false; var bTemp = Context.Busses.Where(b => b.IsDriving) .Select(b => new { b, Passengers = b.Passengers .Where(p => p.Awake) }) .ToList(); foreach(x in bTemp) { xbPasengers = x.Passengers; } var busses = bTemp.Select(x => xb).ToList(); 

…整个事情变得更加吸引人。

第三方工具

有一个库, EntityFramework.DynamicFilters ,这使得这更容易。 它允许您为实体定义全局filter,随后在任何时候查询实体时都会应用这些全局filter。 在你的情况下,这可能看起来像:

 modelBuilder.Filter("Awake", (Person p) => p.Awake, true); 

现在,如果你这样做…

 Context.Busses.Where(b => b.IsDriving) .Include(b => b.People) 

…你会看到filter适用于包含的集合。

您也可以启用/禁用filter,这样您就可以控制应用的时间。 我认为这是一个非常整洁的图书馆。

有一个来自AutoMapper: EntityFramework.Filters的制造商的类似库

entity framework核心

自2.0.0版本以来,EF核心拥有模型级别的查询filter 。 虽然这是对其function的一个很好的补充,但是迄今为止的限制是它不能被应用于导航属性,而只能被应用到查询的根实体。 希望在以后的版本中这些filter将会获得更广泛的用途

免责声明 :我是Entity Framework Plus项目的所有者

EF +查询IncludeFilterfunction允许过滤相关的实体。

 var buses = Context.Busses .Where(b => b.IsDriving) .IncludeFilter(x => x.Passengers.Where(p => p.Awake)) .ToList(); 

Wiki: EF +查询IncludeFilter