我有一个使用Master Detail关系定义的Entity Framwork对象。详细对象集合有一个导航属性。
稍后在代码中我试图使用AutoMapper将其中一个Master对象映射到数据传输对象。但是,数据传输对象需要一个布尔属性,指定记录是否有任何详细记录。
地图正在尝试通过执行以下操作来填充此布尔值:
Mapper.CreateMap<Master, MasterDto>()
.ForMember(dest => dest.HasDetails, src => src.Details.Any())
这大部分时间都有效,但是我有一条主记录有超过200,000个详细记录,当它执行此操作时,它会尝试在运行.Any()之前将所有这些记录从数据库中取出。弄清楚该集合包含任何东西。这需要足够长的时间来超时ASP.NET连接。
有没有办法查询.Details集合是否包含一个值而不先获取所有详细信息行?
答案 0 :(得分:4)
使用延迟加载时,Any
(以及Count
等其他扩展名)会加载整个集合。你无法避免它。使用一些中间类型来选择结果,并将该类型的实例映射到DTO,或直接选择DTO:
context
.Masters
.Select(_ => new MasterDto
{
// ...
HasDetails = _.Details.Any()
});
答案 1 :(得分:0)
我认为这个问题是因为Master已经实现了,在这种情况下,LINQ将使用LINQ to Objects而不是LINQ to Entities进行操作。
答案是映射到将使用.Any()
执行查询的函数src => { return _context.Masters.Where(m => m.Id == src.Id).Any(); }
可能会工作。显然,请将您的上下文引用和Id字段放入。
答案 2 :(得分:0)
虽然它可能看起来很hacky,但你可以抓住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
工厂中)。
显而易见的优点是您根本不需要修改现有的映射/ DTO代码。如果您当前实体中不存在HasDetails
,则可以在类的部分定义中创建它。