EF:包含在where子句中

如标题所示,我正在寻找一种将where子句与include结合使用的方法。

这是我的情况:我负责支持充满代码味道的大型应用程序。更改过多的代码会导致到处都有错误,因此我正在寻找最安全的解决方案。

假设我有一个对象Bus和一个对象People(Bus有一个导航道具Collection of

People)。在我的查询中,我需要选择所有只有醒着的乘客的公共汽车。这是一个简单的虚拟示例

在当前代码中:

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);

}

}

在此代码之后,将处理上下文,并在调用方法中将生成的总线实体映射到DTO类(实体的100%副本)。

此代码导致对DB的多次调用,这是不可行的,因此我在MSDN博客上找到了此解决方案。

这在调试结果时效果很好,但是当实体映射到DTO(使用AutoMapper)时,出现了一个异常,即上下文/连接已关闭并且无法加载该对象。(上下文始终处于关闭状态,无法更改此:()

因此,我需要确保已加载“所选乘客”(导航属性上的“

IsLoaded”也为False)。如果检查乘客集合,则Count也会引发异常,但在Passegers集合上还有一个名为“包装相关实体”的集合,其中包含我的过滤对象。

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

还有另一种方式获得活跃乘客吗?

欢迎任何提示…

回答:

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 => x.b)

.ToList();

回答:

经过大量的努力,Gert Arnold的工作得到了答案!正如Gert

Arnold所建议的那样,您需要禁用“延迟加载”并将其保持关闭状态。由于先前的开发人员喜欢延迟加载-_-,因此这将要求对应用程序进行一些其他更改。

回答:

:此功能现已添加到Entity

Framework核心中。

您可以通过查询以下对象

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 => x.b)

.ToList();

此处发生的情况是,您首先获取了驾驶巴士并从数据库中唤醒了乘客。然后,AsEnumerable()从LINQ切换到Entities,再从LINQ切换到对象,这意味着将实现公共汽车和乘客,然后在内存中对其进行处理。这一点很重要,因为如果没有它,EF只会实现最终的投影Select(x

=> x.b),而不是乘客。

现在,EF具有此功能 关系修补程序 ,可以解决在上下文中实现的对象之间设置所有关联的问题。这意味着Bus现在每个乘客都只被唤醒。

领取ToList公交车时,您便有了想要的乘客的公交车,可以使用AutoMapper对其进行映射。

这仅在禁用延迟加载时有效。否则,在转换为DTO的过程中访问EF时,EF将为每辆巴士延迟加载 所有 乘客。

有两种方法可以禁用延迟加载。LazyLoadingEnabled再次启用时,禁用将重新激活延迟加载。禁用ProxyCreationEnabled将创建无法

自行

延迟加载的实体,因此ProxyCreationEnabled再次启用后它们将不会开始延迟加载。当上下文的寿命比单个查询更长时,这可能是最佳选择。

如前所述,此变通办法依赖于关系修复。然而,正如解释在这里通过Slauma,关系修正不能与许多-

to-many关联工作。如果Bus- Passenger是多对多,则您唯一可以做的就是自己修复它:

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)

{

x.b.Pasengers = x.Passengers;

}

var busses = bTemp.Select(x => x.b).ToList();

……整个事情变得越来越没有吸引力了。

第三方工具

有一个库EntityFramework.DynamicFilters使它变得容易得多。它允许您为实体定义全局过滤器,随后将在查询实体时将其应用。在您的情况下,可能看起来像:

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

现在,如果您愿意…

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

.Include(b => b.People)

…您将看到过滤器已应用于包含的集合。

您还可以启用/禁用过滤器,因此可以控制何时应用它们。我认为这是一个非常整洁的图书馆。

AutoMapper的制造商提供了一个类似的库:EntityFramework.Filters

实体框架核心

从2.0.0版开始,EF-core具有全局查询过滤器。尽管这是对其功能的重要补充,但到目前为止的限制是,过滤器不能包含对导航属性的引用,而不能包含对查询的根实体的引用。希望在更高版本中,这些过滤器将获得更广泛的使用。

过滤的内容是一项长期存在的功能要求。EF核心问题可在此处找到。

以上是 EF:包含在where子句中 的全部内容, 来源链接: utcz.com/qa/419420.html

回到顶部