EF 包括其他实体(通用存储库模式)

Posted

技术标签:

【中文标题】EF 包括其他实体(通用存储库模式)【英文标题】:EF Including Other Entities (Generic Repository pattern) 【发布时间】:2011-07-19 14:02:35 【问题描述】:

我在 Entity Framework Code First 之上使用通用存储库模式。一切正常,直到我需要在查询中包含更多实体。我必须成功包含一个实体,但现在我不知道如何包含多个实体。看看我到目前为止得到了什么:

public IQueryable<TEntity> GetQuery<TEntity>() where TEntity : class

    var entityName = GetEntityName<TEntity>();
    return _objectContext.CreateQuery<TEntity>(entityName);


public IList<TEntity> GetQueryWithInclude<TEntity>(string toInclude) where TEntity : class

    var entityName = GetEntityName<TEntity>();
    return _objectContext.CreateQuery<TEntity>(entityName).Include(toInclude).ToList();


private string GetEntityName<TEntity>() where TEntity : class

    return string.Format("0.1", _objectContext.DefaultContainerName, _pluralizer.Pluralize(typeof(TEntity).Name));

我试图做但没有奏效的是将字符串数组传递给函数,然后尝试在查询顶部“附加”包含。我想知道如果我调用 GetQueryWithInclude 并一次传递一个实体名称(实际上是一个导航属性)来聚合查询结果,但我担心这可能会在每次调用时重复查询结果......您认为实现此功能的最佳方法是什么?

提前致谢!

更新:

这是我想要实现的一个示例:

public IQueryable GetQueryWithIncludes(string[] otherEntities)

    var entityName = GetEntityName<TEntity>();
    //now loop over the otherEntities array 
    //and append Include extensions to the query
    //so inside the loop, something like: 
    _objectContext.GetQuery<TEntity>(entityName).Include(otherEntities[index]);

【问题讨论】:

详细说明“在查询中包含更多实体” ?你能举个例子吗?如果您有ObjectContext,您应该能够使用 LinqToEntities 查询对象/或相关对象 @giddy:查看上面的更新。谢谢。 【参考方案1】:

仅在 IQueryable 上使用 Include 扩展。它在 EF 4.1 程序集中可用。如果您不想在上层中引用该程序集,请在数据访问程序集中创建包装扩展方法。

这里有例子:

public static IQueryable<T> IncludeMultiple<T>(this IQueryable<T> query, params Expression<Func<T, object>>[] includes)
    where T : class

    if (includes != null)
    
        query = includes.Aggregate(query, 
                  (current, include) => current.Include(include));
    

    return query;

您将使用它,例如:

var query = context.Customers
                   .IncludeMultiple(
                       c => c.Address,
                       c => c.Orders.Select(o => o.OrderItems));

此查询将加载所有客户及其地址和订单,每个订单都将包含其订单商品。

【讨论】:

@Ladislav Mrnka:这正是我想要做的。我正在尝试使用 Include 扩展,但我想创建一个通用方法,该方法接受字符串数组中的任意数量的导航属性,然后将这些属性包含在查询实体中。请参阅上面的我的编辑/更新。 不要使用带字符串的版本。 EF 4.1 还提供带有 lambda 的强类型版本。 @Ladislav Mrnka:好的,但是怎么样?可以举个例子吗? 如果您没有从其他人那里得到解决方案,我稍后会发布示例。我现在是用 iPhone 写的,所以提供代码示例非常困难。 @JeffBorden: IncludeMany 是扩展方法(它是静态的,第一个参数使用this 关键字)它适用于所有实现IQueryable 的类。其中包括DbSet【参考方案2】:

//我在这里包括了最低限度。下面是它的使用方法。

     IQueryable<File> xg= UnitOfWork.Files.GetAllLazyLoad(d => d.FileId == 1, 
            r => r.FileCategory);
//where r.FileCategory is a navigational property.

//Interface


    namespace Msh.Intranet.Repository.GenericRepoPattern
    
        public interface IRepository<T> where T:class
        

            IQueryable<T> GetAllLazyLoad(Expression<Func<T, bool>> filter, params Expression<Func<T, object>>[] children);

        
    



        namespace Msh.Intranet.Repository.GenericRepoPattern
        
            /// <summary>
            /// The EF-dependent, generic repository for data access
            /// </summary>
            /// <typeparam name="T">Type of entity for this Repository.</typeparam>
            public class EFRepository<T> : IRepository<T> where T : class
            
                public EFRepository(DbContext dbContext)
                
                    if (dbContext == null)
                        throw new ArgumentNullException("dbContext");
                    DbContext = dbContext;
                    DbSet = DbContext.Set<T>();

                

                protected DbContext DbContext  get; set; 

                protected DbSet<T> DbSet  get; set; 


                public virtual IQueryable<T> GetAllLazyLoad(Expression<Func<T, bool>> filter, params Expression<Func<T, object>>[] children) 
                


                        children.ToList().ForEach(x=>DbSet.Include(x).Load());
                        return DbSet;
                

            
        

【讨论】:

看起来您的示例方法 GetAllLazyLoad 实际上正在急切地加载数据。据我了解,延迟加载将没有包含,在第一次访问时提取数据。您的方法似乎显示的是在创建对象时使用包含提取数据。【参考方案3】:

Say goodbye to the hardcoded ObjectQuery(T).Include calls

如果您使用的是 EF > 4,那么它是内置的,请检查 DbExtensions.Include on MSDN。

【讨论】:

【参考方案4】:

对于实体框架,创建一个字符串列表来保存包含。请参阅下面的示例代码

public virtual IQueryable<TObject> Filter(Expression<Func<TObject, bool>> filter,
                                              int skip = 0,
                                              int take = int.MaxValue,
                                              Func<IQueryable<TObject>, IOrderedQueryable<TObject>> orderBy = null,
                                              IList<string> incudes = null)
    
        var _resetSet = filter != null ? DbSet.AsNoTracking().Where(filter).AsQueryable() : DbSet.AsNoTracking().AsQueryable();

        if (incudes != null)
        
            foreach (var incude in incudes)
            
                _resetSet = _resetSet.Include(incude);
            
        
        if (orderBy != null)
        
            _resetSet = orderBy(_resetSet).AsQueryable();
        
        _resetSet = skip == 0 ? _resetSet.Take(take) : _resetSet.Skip(skip).Take(take);

        return _resetSet.AsQueryable();
    

更多详情请看以下链接 http://bulletproofcoder.com/blog/using-include-in-a-generic-entity-framework-repository

entity framework core中我们将使用IIncludableQueryable。请参阅下面的示例代码

        public virtual IQueryable<TObject> Filter(Expression<Func<TObject, bool>> filter,
                                              int skip = 0,
                                              int take = int.MaxValue,
                                              Func<IQueryable<TObject>, IOrderedQueryable<TObject>> orderBy = null,
                                              Func<IQueryable<TObject>, IIncludableQueryable<TObject, object>> include = null)
    
        var _resetSet = filter != null ? DbSet.AsNoTracking().Where(filter).AsQueryable() : DbSet.AsNoTracking().AsQueryable();

        if (include != null)
        
            _resetSet = include(_resetSet);
        
        if (orderBy != null)
        
            _resetSet = orderBy(_resetSet).AsQueryable();
        
        _resetSet = skip == 0 ? _resetSet.Take(take) : _resetSet.Skip(skip).Take(take);

        return _resetSet.AsQueryable();
    

【讨论】:

您能否详细说明一下。这是一个自写的过滤器方法并使用 IIncludableQueryable?以及你是如何在代码中使用它的? 是的,它是自返回过滤方法。 IIncludableQueryable 是 EF 代码提供的接口。更多细节:docs.microsoft.com/en-us/dotnet/api/… 示例代码(我为我的项目编写): var clientStores = await _unitOfWork.ClientStoreRepository.Filter(predicate, skip: skip, take: take, orderBy: c => c.OrderBy(a => a. StoreName),包括:s => s.Include(l => l.Location).ThenInclude(la => la.LocationArea) .Include(c => c.Country)).ToListAsync();

以上是关于EF 包括其他实体(通用存储库模式)的主要内容,如果未能解决你的问题,请参考以下文章

具有实体框架 4.1 和父/子关系的存储库模式

实体框架使用 Codefirst、通用存储库、工作单元模式保存多对多关系

核心数据编码模式

csharp EF的通用存储库

具有通用存储库和依赖注入和 SoC 的 EF6 Code First

具有 EntityFramework 的通用存储库