使用预加载的存储库通用方法 GetById
Repository generic method GetById using eager loading
我正在使用 Entity Framework 并希望在 Repository class 中创建通用的 GetById 方法并预先加载:
这是我使用延迟加载的方法:
public virtual TEntity GetById(object id)
{
return DbSet.Find(id);
}
我知道 Find 方法不支持预先加载,但是如何修改这个方法来使用预先加载,所以我使用这个方法如下(对于一个示例):
_unitOfWork.MyRepository.GetById(includeProperties: "Users");
一种可能的方法是在 DbSet
和 Include
上使用带有谓词的 FirstOrDefault
。使用 Expression.Equal
方法手动构建谓词并不难,但主要挑战是如何获取键 属性 名称。幸运的是我们可以使用一些 ObjectContext
方法来做到这一点,所以实现可能是这样的(假设我们可以访问具体的 DbContext
实例):
public virtual TEntity GetById(object id, params string[] includeProperties)
{
var propertyName = ((System.Data.Entity.Infrastructure.IObjectContextAdapter)DbContext).ObjectContext
.CreateObjectSet<TEntity>().EntitySet.ElementType.KeyMembers.Single().Name;
var parameter = Expression.Parameter(typeof(TEntity), "e");
var predicate = Expression.Lambda<Func<TEntity, bool>>(
Expression.Equal(
Expression.PropertyOrField(parameter, propertyName),
Expression.Constant(id)),
parameter);
var query = DbSet.AsQueryable();
if (includeProperties != null && includeProperties.Length > 0)
query = includeProperties.Aggregate(query, System.Data.Entity.QueryableExtensions.Include);
return query.FirstOrDefault(predicate);
}
这是 Entity Framework Core 2.0 的更新。此方法还使用带有 EF 的新元数据属性来自动获取第一级导航属性。
public virtual T Get(object id)
{
var propertyName = "AddressId";
//get all navigation properties defined for entity
var navigationProps = _context.Model.FindEntityType(typeof(T)).GetNavigations();
//turn those navigation properties into a string array
var includeProperties = navigationProps.Select(p => p.PropertyInfo.Name).ToArray();
//make parameter of type T
var parameter = Expression.Parameter(typeof(T), "e");
//create the lambda expression
var predicateLeft = Expression.PropertyOrField(parameter, propertyName);
var predicateRight = Expression.Constant(id);
var predicate = Expression.Lambda<Func<T, bool>>(Expression.Equal(predicateLeft, predicateRight), parameter);
//get queryable
var query = _context.Set<T>().AsQueryable();
//apply Include method to the query multiple times
query = includeProperties.Aggregate(query, Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.Include);
//return first item in query
return query.FirstOrDefault(predicate);
}
在存储库中使用它
public IQueryable<T> FindByCondition(Expression<Func<T, bool>> expression)
{
return this.RepositoryContext.Set<T>().Where(expression).AsNoTracking();
}
和这个实战方法
var User = _IRepositoryWrapper.User.FindByCondition(x=>x.Id == id).Include(a=>a.Photos).FirstOrDefault();
我正在使用 Entity Framework 并希望在 Repository class 中创建通用的 GetById 方法并预先加载:
这是我使用延迟加载的方法:
public virtual TEntity GetById(object id)
{
return DbSet.Find(id);
}
我知道 Find 方法不支持预先加载,但是如何修改这个方法来使用预先加载,所以我使用这个方法如下(对于一个示例):
_unitOfWork.MyRepository.GetById(includeProperties: "Users");
一种可能的方法是在 DbSet
和 Include
上使用带有谓词的 FirstOrDefault
。使用 Expression.Equal
方法手动构建谓词并不难,但主要挑战是如何获取键 属性 名称。幸运的是我们可以使用一些 ObjectContext
方法来做到这一点,所以实现可能是这样的(假设我们可以访问具体的 DbContext
实例):
public virtual TEntity GetById(object id, params string[] includeProperties)
{
var propertyName = ((System.Data.Entity.Infrastructure.IObjectContextAdapter)DbContext).ObjectContext
.CreateObjectSet<TEntity>().EntitySet.ElementType.KeyMembers.Single().Name;
var parameter = Expression.Parameter(typeof(TEntity), "e");
var predicate = Expression.Lambda<Func<TEntity, bool>>(
Expression.Equal(
Expression.PropertyOrField(parameter, propertyName),
Expression.Constant(id)),
parameter);
var query = DbSet.AsQueryable();
if (includeProperties != null && includeProperties.Length > 0)
query = includeProperties.Aggregate(query, System.Data.Entity.QueryableExtensions.Include);
return query.FirstOrDefault(predicate);
}
这是 Entity Framework Core 2.0 的更新。此方法还使用带有 EF 的新元数据属性来自动获取第一级导航属性。
public virtual T Get(object id)
{
var propertyName = "AddressId";
//get all navigation properties defined for entity
var navigationProps = _context.Model.FindEntityType(typeof(T)).GetNavigations();
//turn those navigation properties into a string array
var includeProperties = navigationProps.Select(p => p.PropertyInfo.Name).ToArray();
//make parameter of type T
var parameter = Expression.Parameter(typeof(T), "e");
//create the lambda expression
var predicateLeft = Expression.PropertyOrField(parameter, propertyName);
var predicateRight = Expression.Constant(id);
var predicate = Expression.Lambda<Func<T, bool>>(Expression.Equal(predicateLeft, predicateRight), parameter);
//get queryable
var query = _context.Set<T>().AsQueryable();
//apply Include method to the query multiple times
query = includeProperties.Aggregate(query, Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.Include);
//return first item in query
return query.FirstOrDefault(predicate);
}
在存储库中使用它
public IQueryable<T> FindByCondition(Expression<Func<T, bool>> expression)
{
return this.RepositoryContext.Set<T>().Where(expression).AsNoTracking();
}
和这个实战方法
var User = _IRepositoryWrapper.User.FindByCondition(x=>x.Id == id).Include(a=>a.Photos).FirstOrDefault();