EF 包含其他实体(通用仓储模式)

88

我在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 查询对象或相关对象。 - gideon
4个回答

153

仅使用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));

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

3
EF 4.1也提供了使用Lambda表达式的强类型版本,请勿使用带字符串的版本。 - Ladislav Mrnka
如果你没有从其他人那里得到解决方案,我稍后会发布示例。我现在正在iPhone上写作,所以提供代码示例非常困难。 - Ladislav Mrnka
你如何实现IncludeMultiple来扩展DbContext? - Jeff Borden
明白了,我很抱歉理解能力有限。但是默认情况下,DbContext不包括IncludeMany方法。我该如何实现您的方法以开始包含IncludeMany方法?谢谢... - Jeff Borden
1
@JeffBorden: IncludeMany是扩展方法(它是静态的,第一个参数使用this关键字),它适用于所有实现IQueryable接口的类。这包括DbSet - Ladislav Mrnka
显示剩余11条评论

5

3

//这里只包含最基本的内容。下面是如何使用它。

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

            }
        }

2
看起来你的 GetAllLazyLoad 示例方法实际上是急切地加载数据。我的理解是,懒加载应该没有包含关系,第一次访问时只会拉取数据。你的方法似乎是在创建对象时使用了 Include 来拉取数据。 - Bennett Dill

3

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

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

2
你能详细说明一下吗?这是一个自编写的Filter方法并使用了IIncludableQueryable吗?你在代码中是如何使用它的? - liqSTAR
2
是的,这是自返回过滤器方法。IIncludableQueryable是EF代码提供的一个接口。更多细节请参见:https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.query.iincludablequeryable-2?view=efcore-5.0示例代码(我为我的项目编写的): var clientStores = await _unitOfWork.ClientStoreRepository.Filter(predicate, skip: skip, take: take, orderBy: c => c.OrderBy(a => a.StoreName), include: s => s.Include(l => l.Location).ThenInclude(la => la.LocationArea) .Include(c => c.Country)).ToListAsync(); - Bibin Gangadharan
1
你如何使用.ThenInclude(s=>s.Something)进行链式调用? - Bellash

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