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.

的部分定义中创建它