简体   繁体   English

在查询中预先加载的动态包含语句 - EF 4.3.1

[英]Dynamic Include statements for eager loading in a query - EF 4.3.1

I have this method:我有这个方法:

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();
            }
        }

I would like to make the list of Includes dynamic.我想使包含动态列表。 I tried:我试过:

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();                    
            }
        }

But it didn't compile.但它没有编译。 I got this error:我收到此错误:

Cannot implicitly convert type 'System.Data.Entity.Infrastructure.DbQuery' to 'System.Data.Entity.DbSet'.无法将类型“System.Data.Entity.Infrastructure.DbQuery”隐式转换为“System.Data.Entity.DbSet”。 An explicit conversion exists (are you missing a cast?)存在显式转换(您是否缺少转换?)

Does anyone know how to make the list of Includes dynamic?有谁知道如何使包含动态列表?

Thanks谢谢

I am more fond of the non-string expressive way of defining includes.我更喜欢定义包含的非字符串表达方式。 Mainly because it doesn't rely on magic strings.主要是因为它不依赖于魔术弦。

For the example code, it would look something like this:对于示例代码,它看起来像这样:

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();
    }
}

And to make those dynamic, this is how you do that:为了使这些动态,这就是你如何做到的:

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);
    }
}

Which is used like this:这是这样使用的:

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
);

Make the query variable queryable:使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();                    
    }
}

Giving the compiler a hint by using IQueryable<CampaignCreative> instead of var will work too.通过使用IQueryable<CampaignCreative>而不是var给编译器一个提示也可以。

IQueryable<CampaignCreative> query = db.CampaignCreatives;
// or
DbQuery<CampaignCreative> query = db.CampaignCreatives;

When using var the compiler infers DbSet<T> for query which is more specific than the type returned by Include (which is DbQuery<T> (=base class of DbSet<T> ) implementing IQueryable<T> ), so you can't assign the result to the query variable anymore.当使用var时,编译器推断DbSet<T>用于query ,它比Include返回的类型更具体(这是DbQuery<T> (= DbSet DbSet<T> T> 的基础 class )实现IQueryable<T> ),所以你可以'不再将结果分配给query变量。 Hence the compiler error on the query = query.Include(include) line.因此query = query.Include(include)行上的编译器错误。

I wrote this method to retrieve any set of entity dynamically based on their types.我编写此方法是为了根据实体的类型动态检索任何实体集。 I used the IDbEntity interface to provide a valid key to search the userId in all the classes.我使用IDbEntity接口提供了一个有效的键来搜索所有类中的 userId。 The Util.GetInverseProperties<T>() method is used to get the properties needed in the Include statement. 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();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM