问题描述
我知道拥有 Unit Of Work
就是在抽象 (DbContext
) 之上有一个抽象,这肯定是一种反模式,或者至少不是必要的.
I know having a Unit Of Work
is having an abstraction on top of an abstraction (DbContext
) and surely that is an anti-pattern, or at least is not necessary.
我有以下问题:
我有一个像这样的通用 IRepository
:
I have a generic IRepository
like so:
public interface IGenericRepository<TEntity> where TEntity : class
{
IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "");
TEntity GetByID(object id);
void Insert(TEntity entity);
void Delete(object id);
void Delete(TEntity entityToDelete);
void Update(TEntity entityToUpdate);
}
这是这个接口的实现:
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
internal GymHelperContext context;
internal DbSet<TEntity> dbSet;
public GenericRepository(GymHelperContext context)
{
this.context = context;
dbSet = context.Set<TEntity>();
}
public virtual IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
IQueryable<TEntity> query = dbSet;
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}
public virtual TEntity GetByID(object id)
{
return dbSet.Find(id);
}
public virtual void Insert(TEntity entity)
{
dbSet.Add(entity);
}
public virtual void Delete(object id)
{
TEntity entityToDelete = dbSet.Find(id);
Delete(entityToDelete);
}
public virtual void Delete(TEntity entityToDelete)
{
if (context.Entry(entityToDelete).State == EntityState.Detached)
{
dbSet.Attach(entityToDelete);
}
dbSet.Remove(entityToDelete);
}
public virtual void Update(TEntity entityToUpdate)
{
dbSet.Attach(entityToUpdate);
context.Entry(entityToUpdate).State = EntityState.Modified;
}
}
我有一个名为 Facade 的项目,它实例化 Mapper 和 UnitOfWork,如下所示:
I have a proyect called Facade that instantiates a Mapper and a UnitOfWork, like so:
public class MuscleFacade
{
private readonly UnitOfWork _unitOfWork = new UnitOfWork();
private readonly MuscleMapping _muscleMapping = new MuscleMapping();
public MuscleFacade()
{
}
public IEnumerable<MuscleViewModel> GetAllMuscles()
{
var source = _unitOfWork.MuscleRepository
.Get()
.ToList();
var result = source.Select(x => _muscleMapping.MuscleToModel(x));
return result;
}
public GymViewModel GetGymViewModel()
{
GymViewModel gymViewModel = new GymViewModel
{
ListOfMuscles = GetAllMuscles().ToList()
};
return gymViewModel;
}
}
MuscleFacade
类是我使用 Autofac 在我的控制器上注入的,我在其构造函数中注入了一个 IMuscleFacade
.
The MuscleFacade
class it's what I inject on my controller with Autofac, I inject an IMuscleFacade
in its constructor.
现在的问题是,我的 MuscleTypeViewModel
有一个 MuscleViewModel
列表,这些模型与它们对应的 Domain 类映射,在这种特殊情况下是 MuscleType
有很多 Muscle
(例如:手臂有二头肌、三头肌等),所以我将导航属性放在每个上面,如下所示:
Now the thing is, my MuscleTypeViewModel
have a list of MuscleViewModel
these models are mapped with their Domain classes counterparts, and in this particular case a MuscleType
have many Muscle
(Eg: Arm have bicep, tricep, etc) so I put navigational properties on each of them, like so:
public class MuscleType : IEntity
{
public int Id { get; set; }
[StringLength(100)]
public string MuscleTypeName { get; set; }
public ICollection<Muscle> Muscles { get; set; }
}
public class Muscle : IEntity
{
public int Id { get; set; }
[StringLength(100)]
public string MuscleName { get; set; }
public int MuscleTypeId { get; set; }
public MuscleType MuscleType { get; set; }
}
现在让我们再看一下 Facade 中的 GetAllMuscles
方法:
Now let's look at GetAllMuscles
method in the Facade again:
public IEnumerable<MuscleViewModel> GetAllMuscles()
{
var source = _unitOfWork.MuscleRepository
.Get()
.ToList();
var result = source.Select(x => _muscleMapping.MuscleToModel(x));
return result;
}
如果我想要 Eager-Load MuscleType 怎么办,如何更改 Get() 以便接收 Func
的 Expression
而不是 string
?
What if I want to Eager-Load MuscleType, how can I change the Get() in order to receive and Expression
of Func
instead of a string
?
推荐答案
您可以定义一个包含您的包含定义的帮助类:
You can define a helper class that contains your include definitions:
abstract class IncludeDefinition<TEntity>
{
public abstract IQueryable<TEntity> Include(IQueryable<TEntity> entities);
}
class IncludeDefinition<TEntity, TProperty> : IncludeDefinition<TEntity>
{
public IncludeDefinition(Expression<Func<TEntity, TProperty>> includeEx)
{
_includeEx = includeEx;
}
private readonly Expression<Func<TEntity, TProperty>> _includeEx;
public override IQueryable<TEntity> Include(IQueryable<TEntity> entities)
{
return entities.Include(_includeEx);
}
}
然后在你的 Get
方法中使用 IncludeDefinition
Then use the IncludeDefinition
in your Get
method
public IEnumerable<Muscle> Get(params IncludeDefinition<Muscle>[] includes)
{
IQueryable<Muscle> muscles = ...;
foreach (var item in includes)
{
muscles = item.Include(muscles);
}
return muscles.ToList();
}
并调用方法
_unitOfWork.MuscleRepository
.Get(new IncludeDefinition<Muscle, MuscleType>(m => m.MuscleType));
// Include as many as you wish
_unitOfWork.MuscleRepository
.Get(new IncludeDefinition<Muscle, MuscleType>(m => m.MuscleType),
new IncludeDefinition<Muscle, SomeOtherRelatedEntity>(m => m.SomeOtherProperty));
编辑这里有一些方法可以只包含",而不是编写复杂的语法.
Edit here comes some way to "just include" instead of writing complicated syntax.
创建一个新的接口IQueryRepository
,它支持Get
,无需显式包含和Include
,从这个接口派生IGenericRepository
:
Create a new interface IQueryRepository
that supports Get
without explicit includes and Include
, derive IGenericRepository
from this interface:
public interface IQueryRepository<TEntity>
where TEntity : class
{
IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null);
IQueryRepository<TEntity> Include<TProperty>(Expression<Func<TEntity, TProperty>> referenceExpression);
}
public interface IGenericRepository<TEntity> : IQueryRepository<TEntity>
where TEntity : class
{
IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
params IncludeDefinition<TEntity>[] include);
// other methods like GetByID, Add, Update...
}
更新 GenericRepository
定义 - 它使用我最初描述的带有 IncludeDefinition
的方法,并在 Include<时返回
GenericQueryRepositoryHelper
/code> 被调用.
Update the GenericRepository
definition - it uses the approach with IncludeDefinition
that I initially described and it returns a GenericQueryRepositoryHelper
when Include
is called.
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
internal DbSet<TEntity> dbSet;
public IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null)
{
return Get(filter, orderBy, new IncludeDefinition<TEntity>[0]);
}
public IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, params IncludeDefinition<TEntity>[] includes)
{
IQueryable<TEntity> query = dbSet;
foreach (var item in includes)
{
query = item.Include(query);
}
if (filter != null)
{
query = query.Where(filter);
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}
public IQueryRepository<TEntity> Include<TProperty>(Expression<Func<TEntity, TProperty>> referenceExpression)
{
return new GenericQueryRepositoryHelper<TEntity>(this, new IncludeDefinition<TEntity, TProperty>(referenceExpression));
}
// other methods like GetByID, Add, Update...
}
实现 GenericQueryRepositoryHelper
来存储包含并在调用 Get
时应用它们
Implement the GenericQueryRepositoryHelper
to store includes and apply them when Get
is called
public class GenericQueryRepositoryHelper<TEntity> : IQueryRepository<TEntity>
where TEntity : class
{
private readonly IList<IncludeDefinition<TEntity>> _includeDefinitions;
private readonly IGenericRepository<TEntity> _repository;
internal GenericQueryRepositoryHelper(IGenericRepository<TEntity> repository, IncludeDefinition<TEntity> includeDefinition)
{
_repository = repository;
_includeDefinitions = new List<IncludeDefinition<TEntity>> { includeDefinition };
}
public IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null)
{
return _repository.Get(filter, orderBy, _includeDefinitions.ToArray());
}
public IQueryRepository<TEntity> Include<TProperty>(Expression<Func<TEntity, TProperty>> referenceExpression)
{
_includeDefinitions.Add(new IncludeDefinition<TEntity, TProperty>(referenceExpression));
return this;
}
}
愉快的查询包括:
var repo = new GenericRepository<Muscle>(...);
repo.Include(x => x.MuscleType)
.Include(x => x.MuscleType.Muscles)
.Get(x => x.MuscleName == "Test", x => x.OrderBy(m => m.MuscleName));
这篇关于IRepository 和工作单元与急切加载的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!