将包含与实体框架相结合

时间:2013-02-18 11:25:32

标签: entity-framework lazy-loading

我通常使用通用存储库来编写我的EF查询,因此我必须编写有限的代码并使用缓存。可以找到存储库的源代码here

代码中的骨干查询如下所示。 FromCache<T>()是一种IEnumerable<T>扩展方法,它使用HttpContext.Cache以lambda表达式的字符串表示形式存储查询作为键。

    public IQueryable<T> Any<T>(Expression<Func<T, bool>> expression = null)
    where T : class, new()
    {
        // Check for a filtering expression and pull all if not.
        if (expression == null)
        {
            return this.context.Set<T>()
                       .AsNoTracking()
                       .FromCache<T>(null)
                       .AsQueryable();
        }

        return this.context.Set<T>()
                           .AsNoTracking<T>()
                           .Where<T>(expression)
                           .FromCache<T>(expression)
                           .AsQueryable<T>();
    }

虽然这一切都有效,但它会受到相关表的N + 1问题的影响,因为如果我要编写这样的查询:

var posts = this.ReadOnlySession.Any<Post>(p => p.IsDeleted == false)
                                .Include(p => p.Author);

Include()对我的查询没有任何影响,因为它已经被运行以便进行缓存。

现在我知道我可以通过删除导航属性上的虚拟前缀来强制Entity Framework在我的模型中使用急切加载,但这对我来说感觉就像是错误的地方,因为你无法预测查询类型正在制作。对我来说,感觉就像我将在控制器类中做的事情。我想知道的是,我是否可以将包含列表传递给我的Any<T>()方法,然后我可以在拨打电话时进行迭代?

2 个答案:

答案 0 :(得分:1)

ofDid你的意思是......

IQueryable<T> AnyWithInclude<T,I>(Expression<Func<T,bool>> predicate,
                                  Expression<Func<T,I>> includeInfo) 
{ 
  return  DbSet<T>.where(predicate).include(includeInfo);
}

电话

Context.YourDbSetReference.AnyWithInclude(t => t.Id==someId, i => i.someNavProp);

回应关于收集的额外问题。

我意识到很晚,物业出现了过载。你可以传递一个字符串

这可能有用,但打电话并不容易。好吧,我觉得很难。

   IQueryable<T> GetListWithInclude<I>(Expression<Func<T, bool>> predicate,
                                        params Expression<Func<T, I>>[] IncludeCollection);

所以我试过

 public virtual IQueryable<T> GetListWithInclude(Expression<Func<T, bool>> predicate, 
                                                 List<string> includeCollection)
    { var result = EntityDbSet.Where(predicate);
        foreach (var incl in includeCollection)
        {
            result = result.Include(incl);
        }
        return result;
    }

并使用

进行调用
 var ic = new List<string>();
 ic.Add("Membership");
 var res =  context.DbSte<T>.GetListWithInclude( t=>t.UserName =="fred", ic);

像以前一样工作。

答案 1 :(得分:0)

为了清楚起见,我根据@ soadyp的回答添加了我想出的解决方案。

public IQueryable<T> Any<T>(Expression<Func<T, bool>> expression = null, 
                         params Expression<Func<T, object>>[] includeCollection)
    where T : class, new()
{
    IQueryable<T> query = this.context.Set<T>().AsNoTracking().AsQueryable<T>();

    if (includeCollection.Any())
    {
        query = includeCollection.Aggregate(query, 
                    (current, include) => current.Include(include));
    }

    // Check for a filtering expression and pull all if not.
    if (expression != null)
    {
        query = query.Where<T>(expression);
    }

    return query.FromCache<T>(expression, includeCollection)
                .AsQueryable<T>();
}

用法:

// The second, third, fourth etc parameters are the strongly typed includes.
var posts = this.ReadOnlySession.Any<Post>(p => p.IsDeleted == false,
                                           p => p.Author);