Bee*_*jee 45 c# entity-framework include where-clause
正如标题所示,我正在寻找一种方法来将where子句与include结合使用.
这是我的情况:我负责支持一个充满代码味道的大型应用程序.更改过多代码会导致各处出现错误,因此我正在寻找最安全的解决方案.
假设我有一个对象总线和一个对象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);
}
}
Run Code Online (Sandbox Code Playgroud)
在此代码之后,处理Context,并且在调用方法中,生成的Bus实体被映射到DTO类(实体的100%副本).
此代码导致多次调用DB,这是一个禁止,所以我在MSDN博客上找到了这个解决方案
这在调试结果时效果很好,但是当实体映射到DTO时(使用AutoMapper),我得到一个异常,即Context/Connection已经关闭并且无法加载该对象.(上下文总是关闭不能改变这个:()
所以我需要确保已经加载了Selected Passengers(导航属性上的IsLoaded也是False).如果我检查Passengers集合,Count也会抛出Exception,但是在Passegers集合中还有一个名为"包装相关实体"的集合,其中包含我的过滤对象.
有没有办法将这些包装的相关实体加载到整个集合中?(我无法更改automapper mapping配置,因为它在整个应用程序中使用).
有没有其他方式来获得活跃的乘客?
任何提示都是受欢迎的......
Gert Arnold的答案不起作用,因为数据未被急切加载.但是当我简化它并删除它的加载位置时.这真是奇怪,因为执行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();
Run Code Online (Sandbox Code Playgroud)
经过很多努力,格特阿诺德的答案奏效了!正如Gert Arnold建议您需要禁用延迟加载并将其保持关闭状态.这将要求对应用程序进行一些额外的更改,因为上一代开发人员喜欢Lazy Loading -_-
Ger*_*old 54
您可以通过查询所需的对象
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();
Run Code Online (Sandbox Code Playgroud)
这里发生的是你首先从数据库中取出驾驶巴士和唤醒乘客.然后,AsEnumerable()
从LINQ切换到实体到LINQ到对象,这意味着总线和乘客将被实现,然后在内存中处理.这很重要,因为没有它,EF只会实现最终投影Select(x => x.b)
,而不是乘客.
现在,EF具有此功能关系修正,负责设置在上下文中实现的对象之间的所有关联.这意味着Bus
现在每个人只有清醒的乘客才会被装上.
当您获得公共汽车的集合时,ToList
您可以使用您想要的乘客的公共汽车,您可以使用AutoMapper进行映射.
这仅在禁用延迟加载时有效.否则当转换到DTO期间访问乘客时,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();
Run Code Online (Sandbox Code Playgroud)
......整个事情变得更加吸引人了.
有一个库,EntityFramework.DynamicFilters,使这更容易.它允许您为实体定义全局过滤器,随后将在查询实体时应用这些过滤器.在您的情况下,这可能看起来像:
modelBuilder.Filter("Awake", (Person p) => p.Awake, true);
Run Code Online (Sandbox Code Playgroud)
现在,如果你这样做......
Context.Busses.Where(b => b.IsDriving)
.Include(b => b.People)
Run Code Online (Sandbox Code Playgroud)
...您将看到过滤器已应用于包含的集合.
您还可以启用/禁用过滤器,以便您可以控制何时应用它们.我认为这是一个非常简洁的图书馆.
AutoMapper的制造商有一个类似的库:EntityFramework.Filters
从版本2.0.0开始,EF-core具有全局查询过滤器.虽然这是对其功能的一个很好的补充,但到目前为止,限制是过滤器不能包含对导航属性的引用,只能包含对查询的根实体的引用.希望在以后的版本中这些过滤器将获得更广泛的使用.
过滤包含是一项长期的功能请求.EF核心问题可以在这里找到.
Sat*_*pal 24
现在 EF Core 5.0的Filter Include方法现在支持过滤包含的实体
var busses = _Context.Busses
.Include(b => b.Passengers
.Where(p => p.Awake))
.Where(b => b.IsDriving);
Run Code Online (Sandbox Code Playgroud)
Jon*_*nan 21
免责声明:我是项目Entity Framework Plus的所有者
EF + Query IncludeFilter功能允许过滤相关实体.
var buses = Context.Busses
.Where(b => b.IsDriving)
.IncludeFilter(x => x.Passengers.Where(p => p.Awake))
.ToList();
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
47876 次 |
最近记录: |