EntityFramework确定Child集合是否包含没有获取整个集合的项目

时间:2015-10-26 12:48:09

标签: c# entity-framework automapper

我有一个使用Master Detail关系定义的Entity Framwork对象。详细对象集合有一个导航属性。

稍后在代码中我试图使用AutoMapper将其中一个Master对象映射到数据传输对象。但是,数据传输对象需要一个布尔属性,指定记录是否有任何详细记录。

地图正在尝试通过执行以下操作来填充此布尔值:

Mapper.CreateMap<Master, MasterDto>()
    .ForMember(dest => dest.HasDetails, src => src.Details.Any())

这大部分时间都有效,但是我有一条主记录有超过200,000个详细记录,当它执行此操作时,它会尝试在运行.Any()之前将所有这些记录从数据库中取出。弄清楚该集合包含任何东西。这需要足够长的时间来超时ASP.NET连接。

有没有办法查询.Details集合是否包含一个值而不先获取所有详细信息行?

3 个答案:

答案 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,则可以在类的部分定义中创建它。