如何为两个独立领域创建Generic Repository避免新增额外依赖?
方案1:调整通用仓储设计,移除对统一基类的硬依赖
这是完全符合要求的最优解,不需要领域项目引入任何外部依赖,也不需要创建公共基础项目:
你可以把通用仓储对BaseEntity的继承约束,替换为初始化时传入Id属性的访问委托,示例代码如下:
public class GenericRepository<T> { private readonly Func<T, int> _getIdSelector; // 构造函数接收对应实体的Id获取逻辑 public GenericRepository(Func<T, int> getIdSelector) { _getIdSelector = getIdSelector; } public T SomeActionThatRequiresId(int id) { // 此处直接通过委托获取实体Id,不需要T继承任何基类或接口 } }
使用时你只需要在两个独立的DataAccess项目中,为各自的实体初始化仓储时传入对应Id的选择器即可,比如:
// 领域1的仓储初始化 var orderRepository = new GenericRepository<Order>(o => o.Id); // 领域2的仓储初始化 var customerRepository = new GenericRepository<Customer>(c => c.Id);
这个方案完全满足DRY原则,仓储的核心逻辑只需要实现一次,两个DataAccess项目都可以复用,同时领域项目不需要做任何修改,也不需要引入任何外部依赖。
方案2:使用接口约束+源生成器自动适配
如果你更倾向于用类型约束来做编译级检查,不想传委托,可以选择这个方案:
- 在通用仓储项目中定义一个只有Id属性的接口
IHasId,作为泛型约束:
public interface IHasId { int Id { get; } } public class GenericRepository<T> where T : IHasId { public T SomeActionThatRequiresId(int id) { // 直接通过IHasId.Id访问属性 } }
- 写一个极简单的源生成器,扫描两个领域项目中的实体,只要实体有Id属性,自动给它加上
IHasId接口的实现,不需要修改领域项目的代码,也不需要领域项目引用仓储项目。
这个方案可以保留类型安全的编译检查,同时也不会破坏领域项目无外部依赖的规范。
临时备选方案
如果你的项目规模很小,也可以选择把同样的BaseEntity类分别复制到两个领域项目中,统一命名空间即可。不过这种方案违反DRY原则,后续修改需要同步两处,仅适合临时使用。
内容的提问来源于stack exchange,提问作者Adam Kaczmarski
相关产品推荐
相关产品推荐

