背景/问题
我参与过许多需要持久化数据并且通常最终使用存储库模式的 .NET 项目。有谁知道在不牺牲代码库可扩展性的情况下删除尽可能多的样板代码的好策略?
继承策略
因为很多存储库代码都是样板并且需要重复,所以我通常创建一个基类来涵盖异常处理、日志记录和事务支持等基础知识以及一些基本的 CRUD 方法:
public abstract class BaseRepository<T> where T : IEntity
{
protected void ExecuteQuery(Action query)
{
//Do Transaction Support / Error Handling / Logging
query();
}
//CRUD Methods:
public virtual T GetByID(int id){}
public virtual IEnumerable<T> GetAll(int id){}
public virtual void Add (T Entity){}
public virtual void Update(T Entity){}
public virtual void Delete(T Entity){}
}
所以当我有一个简单的域时,这很有效,我可以为每个实体快速创建一个 DRY 存储库类。但是,当域变得更加复杂时,这种情况就会开始崩溃。假设引入了一个不允许更新的新实体。我可以分解基类并将 Update 方法移动到不同的类中:
public abstract class BaseRepositorySimple<T> where T : IEntity
{
protected void ExecuteQuery(Action query);
public virtual T GetByID(int id){}
public virtual IEnumerable<T> GetAll(int id){}
public virtual void Add (T entity){}
public void Delete(T entity){}
}
public abstract class BaseRepositoryWithUpdate<T> :
BaseRepositorySimple<T> where T : IEntity
{
public virtual void Update(T entity){}
}
此解决方案不能很好地扩展。假设我有几个具有通用方法的实体: public virtual void Archive(T entity){}
但是一些可以存档的实体也可以更新,而其他实体则不能。所以我的继承解决方案崩溃了,我必须创建两个新的基类来处理这种情况。
作曲策略
我已经探索了 Compositon 模式,但这似乎留下了很多样板代码:
public class MyEntityRepository : IGetByID<MyEntity>, IArchive<MyEntity>
{
private Archiver<MyEntity> _archiveWrapper;
private GetByIDRetriever<MyEntity> _getByIDWrapper;
public MyEntityRepository()
{
//initialize wrappers (or pull them in
//using Constructor Injection and DI)
}
public MyEntity GetByID(int id)
{
return _getByIDWrapper(id).GetByID(id);
}
public void Archive(MyEntity entity)
{
_archiveWrapper.Archive(entity)'
}
}
MyEntityRepository 现在加载了样板代码。有没有可以用来自动生成它的工具/模式?
如果我可以将 MyEntityRepository 变成这样的东西,我认为这将是理想的:
[Implement(Interface=typeof(IGetByID<MyEntity>),
Using = GetByIDRetriever<MyEntity>)]
[Implement(Interface=typeof(IArchive<MyEntity>),
Using = Archiver<MyEntity>)
public class MyEntityRepository
{
public MyEntityRepository()
{
//initialize wrappers (or pull them in
//using Constructor Injection and DI)
}
}
面向方面的编程
我研究了为此使用 AOP 框架,特别是PostSharp和他们的Composition Aspect,看起来它应该可以解决问题,但是为了使用存储库,我必须调用 Post.Cast<>(),它添加了一个非常奇怪的代码气味。任何人都知道是否有更好的方法来使用 AOP 来帮助摆脱合成器样板代码?
自定义代码生成器
如果一切都失败了,我想我可以创建一个自定义代码生成器 Visual Studio 插件,它可以将样板代码生成到部分代码文件中。是否已经有一个工具可以做到这一点?
[Implement(Interface=typeof(IGetByID<MyEntity>),
Using = GetByIDRetriever<MyEntity>)]
[Implement(Interface=typeof(IArchive<MyEntity>),
Using = Archiver<MyEntity>)
public partial class MyEntityRepository
{
public MyEntityRepository()
{
//initialize wrappers (or pull them in
//using Constructor Injection and DI)
}
}
//Generated Class file
public partial class MyEntityRepository : IGetByID<MyEntity>, IArchive<MyEntity>
{
private Archiver<MyEntity> _archiveWrapper;
private GetByIDRetriever<MyEntity> _getByIDWrapper;
public MyEntity GetByID(int id)
{
return _getByIDWrapper(id).GetByID(id);
}
public void Archive(MyEntity entity)
{
_archiveWrapper.Archive(entity)'
}
}
扩展方法
当我最初写这个问题时忘记添加这个(对不起)。我还尝试了扩展方法:
public static class GetByIDExtenions
{
public T GetByID<T>(this IGetByID<T> repository, int id){ }
}
但是,这有两个问题,a)我必须记住扩展方法类的名称空间并将其添加到任何地方,b)扩展方法不能满足接口依赖项:
public interface IMyEntityRepository : IGetByID<MyEntity>{}
public class MyEntityRepository : IMyEntityRepository{}
更新:T4 模板会是一个可能的解决方案吗?