动态包含语句用于在查询中进行急切加载 - EF 4.3.1

16

我有这个方法:

public CampaignCreative GetCampaignCreativeById(int id)
        {
            using (var db = GetContext())
            {
                return db.CampaignCreatives
                    .Include("Placement")
                    .Include("CreativeType")                    
                    .Include("Campaign")
                    .Include("Campaign.Handshake")
                    .Include("Campaign.Handshake.Agency")
                    .Include("Campaign.Product")
                    .AsNoTracking()
                    .Where(x => x.Id.Equals(id)).FirstOrDefault();
            }
        }

我想把包含的列表变成动态的。我尝试了:

public CampaignCreative GetCampaignCreativeById(int id, string[] includes)
        {
            using (var db = GetContext())
            {
                var query = db.CampaignCreatives;

                foreach (string include in includes)
                {
                    query = query.Include(include);
                }

                return query.AsNoTracking()
                    .Where(x => x.Id.Equals(id)).FirstOrDefault();                    
            }
        }

但是它没有编译通过。我收到了这个错误:

无法隐式转换类型 'System.Data.Entity.Infrastructure.DbQuery' 为类型 'System.Data.Entity.DbSet'。存在显式转换(是否缺少强制转换?)

有人知道如何使 Includes 列表动态化吗?

谢谢


我已经做了一个插件,可以实现这个功能,这是链接 https://www.codeproject.com/Tips/1205294/Entity-Framework-Dynamic-Include-Hierarchy - Alen.Toma
4个回答

39

我更喜欢使用非字符串表达式定义包含方式,主要是因为它不依赖于魔法字符串。

以代码示例为例,它可能看起来像这样:

public CampaignCreative GetCampaignCreativeById(int id) {
    using (var db = GetContext()) {
        return db.CampaignCreatives
            .Include(cc => cc.Placement)
            .Include(cc => cc.CreativeType)                    
            .Include(cc => cc.Campaign.Select(c => 
                 c.Handshake.Select(h => h.Agency)))
            .Include(cc => cc.Campaign.Select(c => c.Product)
            .AsNoTracking()
            .Where(x => x.Id.Equals(id))
            .FirstOrDefault();
    }
}

要使它们动态化,您可以按照以下步骤操作:

public CampaignCreative GetCampaignCreativeById(
    int id, 
    params Expression<Func<T, object>>[] includes
) {
    using (var db = GetContext()) {
        var query = db.CampaignCreatives;
        return includes
            .Aggregate(
                query.AsQueryable(), 
                (current, include) => current.Include(include)
            )
            .FirstOrDefault(e => e.Id == id);
    }
}

以下是使用示例:

var c = dataService.GetCampaignCreativeById(
     1, 
     cc => cc.Placement, 
     cc => cc.CreativeType, 
     cc => cc.Campaign.Select(c => c.Handshake.Select(h => h.Agency)),
     cc => cc.Campaign.Select(c => c.Product
);

1
我之前已经说过了,但为了防止有人错过,我在这里再说一遍:使用这个解决方案时,有些项目会出现编译错误。请添加 using System.Data.Entity; 以访问 IQueryable 上的 Include 扩展方法。 - erstaples
这个答案应该被选中。当涉及到泛型类型上的EF强类型导航属性时,我发现它非常有用。 - dotnetspark
但是当你看到这个LINQ的转换后的SQL语句时,上面的includes总是显示内连接。是否有一种方法可以使用相同的includes方法实现左连接或右连接?为了参考,我包含了我的帖子链接https://stackoverflow.com/questions/55129505/how-to-implement-left-join-on-lambda-linq-expression-with-includes 谢谢。 - LOKI

19

query变量设为可查询:

public CampaignCreative GetCampaignCreativeById(int id, string[] includes)
{
    using (var db = GetContext())
    {
        var query = db.CampaignCreatives.AsQueryable();
        foreach (string include in includes)
        {
            query = query.Include(include);
        }

        return query
            .AsNoTracking()
            .Where(x => x.Id.Equals(id))
            .FirstOrDefault();                    
    }
}

我不明白。IQueryable没有Include方法。这无法编译。 - dudeNumber4
在一些项目中,你会发现使用这个解决方案会出现编译错误。请添加 using System.Data.Entity; 以便访问 IQueryable 上的 Include 扩展方法。 - erstaples
这在EF4.0中不起作用。缺少Include扩展。 - Davut Gürbüz
IQuerable在EF4.0中没有Include扩展。对于EF4.0,您需要将IQuerable<T>相反地转换为ObjectQuery<T>。然后您就可以包含细节了。 - Davut Gürbüz
有人有嵌套包含的代码片段吗? - Vasil Valchev

2
使用 IQueryable<CampaignCreative> 而不是 var,可以给编译器一个提示,这也是可行的。
IQueryable<CampaignCreative> query = db.CampaignCreatives;
// or
DbQuery<CampaignCreative> query = db.CampaignCreatives;

当使用var时,编译器会为query推断出更具体的类型DbSet<T>,而不是由Include返回的类型(即实现IQueryable<T>DbQuery<T>(= DbSet<T>的基类)),因此您不能再将结果分配给query变量。因此,在query = query.Include(include)行上出现编译器错误。


1
我编写了这个方法,可以根据它们的类型动态地检索任何实体集。我使用了 IDbEntity 接口来提供一个有效的键,在所有类中搜索 userId。 Util.GetInverseProperties<T>() 方法用于获取在 Include 语句中所需的属性。
public IEnumerable<T> GetItems<T>(string userId) where T : class, IDbEntity
{
    var query = db.Set<T>().Where(l => l.UserId==userId);

    var props = Util.GetInverseProperties<T>();
    foreach (var include in props)
        query = query.Include(include.Name);

    return query
        .AsNoTracking()
        .ToList();
}

public interface IDbEntity
{
    public string UserId { get; set; }
}

public static List<PropertyInfo> GetInverseProperties<T>()
{
    return typeof(T)
        .GetProperties()
        .Where(p => Attribute.IsDefined(p, typeof(InversePropertyAttribute)))
        .ToList();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接