EntityFramework 在不获取整个集合的情况下确定子集合是否包含项目
EntityFramework determining if Child collection contains items without fetching the whole collection
我有一个定义了主从关系的实体框架对象。详细对象集合有一个导航属性。
在稍后的代码中,我尝试使用 AutoMapper 将主对象之一映射到数据传输对象。然而,数据传输对象需要一个布尔值 属性 来指定记录是否有任何详细记录。
地图正尝试通过执行以下操作来填充此布尔值:
Mapper.CreateMap<Master, MasterDto>()
.ForMember(dest => dest.HasDetails, src => src.Details.Any())
这在大多数情况下都有效,但我有一个包含超过 200,000 条详细记录的主记录,当它开始执行此映射时,它试图在 [=25= 之前将所有这些都从数据库中取出] .Any() 找出集合中包含的任何东西。这花费的时间足以使 ASP.NET 连接超时。
有什么方法可以查询 .Details 集合是否包含一个值,而无需首先获取所有详细信息行?
使用延迟加载时,Any
(以及其他扩展,如 Count
)加载整个集合。你无法避免它。使用某种中间类型来 select 结果,并将该类型的实例映射到 DTO,或者直接 select DTO:
context
.Masters
.Select(_ => new MasterDto
{
// ...
HasDetails = _.Details.Any()
});
我想这个问题是因为 Master 已经具体化,在这种情况下,LINQ 将使用 LINQ to Objects 而不是 LINQ to Entities 进行操作。
答案是映射到将使用 .Any()
执行查询的函数
src => { return _context.Masters.Where(m => m.Id == src.Id).Any(); }
可能 work.Obviously,将您的上下文参考和 Id 字段放入。
虽然它看起来很老套,但您可以赶上 ObjectMaterialized
事件:
((IObjectContextAdapter)yourDbContext).ObjectContext.ObjectMaterialized += (sender, e) =>
{
var entityAsMaster = e.Entity as Master;
if (entityAsMaster != null)
{
entityAsMaster.HasDetails = this.context
.Entry(entityAsMaster)
.Collection(z => z.Details)
.Query()
.Any();
}
};
(此代码可能位于您的 DbContext
工厂中)。
明显的优势是您根本不需要修改现有的 mapping/DTO 代码。如果 HasDetails
在您当前的实体中不存在,您可以在 class.
的部分定义中创建它
我有一个定义了主从关系的实体框架对象。详细对象集合有一个导航属性。
在稍后的代码中,我尝试使用 AutoMapper 将主对象之一映射到数据传输对象。然而,数据传输对象需要一个布尔值 属性 来指定记录是否有任何详细记录。
地图正尝试通过执行以下操作来填充此布尔值:
Mapper.CreateMap<Master, MasterDto>()
.ForMember(dest => dest.HasDetails, src => src.Details.Any())
这在大多数情况下都有效,但我有一个包含超过 200,000 条详细记录的主记录,当它开始执行此映射时,它试图在 [=25= 之前将所有这些都从数据库中取出] .Any() 找出集合中包含的任何东西。这花费的时间足以使 ASP.NET 连接超时。
有什么方法可以查询 .Details 集合是否包含一个值,而无需首先获取所有详细信息行?
使用延迟加载时,Any
(以及其他扩展,如 Count
)加载整个集合。你无法避免它。使用某种中间类型来 select 结果,并将该类型的实例映射到 DTO,或者直接 select DTO:
context
.Masters
.Select(_ => new MasterDto
{
// ...
HasDetails = _.Details.Any()
});
我想这个问题是因为 Master 已经具体化,在这种情况下,LINQ 将使用 LINQ to Objects 而不是 LINQ to Entities 进行操作。
答案是映射到将使用 .Any()
执行查询的函数src => { return _context.Masters.Where(m => m.Id == src.Id).Any(); }
可能 work.Obviously,将您的上下文参考和 Id 字段放入。
虽然它看起来很老套,但您可以赶上 ObjectMaterialized
事件:
((IObjectContextAdapter)yourDbContext).ObjectContext.ObjectMaterialized += (sender, e) =>
{
var entityAsMaster = e.Entity as Master;
if (entityAsMaster != null)
{
entityAsMaster.HasDetails = this.context
.Entry(entityAsMaster)
.Collection(z => z.Details)
.Query()
.Any();
}
};
(此代码可能位于您的 DbContext
工厂中)。
明显的优势是您根本不需要修改现有的 mapping/DTO 代码。如果 HasDetails
在您当前的实体中不存在,您可以在 class.