代码之家  ›  专栏  ›  技术社区  ›  James South

将Includes与实体框架相结合

  •  1
  • James South  · 技术社区  · 13 年前

    我通常使用通用存储库来样板我的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 回复  |  直到 13 年前
        1
  •  1
  •   phil soady    13 年前

    你的意思是。。。

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

    和以前一样工作。

        2
  •  0
  •   James South    13 年前

    为了清晰起见,我添加了我根据@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);