C#存储库设计问题

时间:2010-11-14 22:42:46

标签: c# inheritance abstract-class repository-pattern

我正在为MVC 2 Web应用程序编写EF4数据层,我需要有关选择继承与抽象基类的建议。我的存储库在'generic repo'结构之后运行良好,但现在我想添加“Audit”功能,该功能在每次执行CRUD操作时都会记录。

这是我到目前为止使用的合同:

public interface IRepository<T>
{
    void Create(T entity);
    void Update(T entity);
    void Delete(Func<T, bool> predicate);
    T Get(Func<T, bool> predicate);
    IQueryable<T> Query();
}

我的回购实现看起来像这样:

sealed class EFRepository<TEntity> : IRepository<TEntity>
    where TEntity : EntityObject
{
    ObjectContext _context;
    ObjectSet<TEntity> _entitySet;

    public EFRepository(ObjectContext context)
    {
        _context = context;
        _entitySet = _context.CreateObjectSet<TEntity>();
    }

    public void Create(TEntity entity)
    {
        _entitySet.AddObject(entity);
        _context.SaveChanges();
    }

    public void Update(TEntity entity)
    {
        _entitySet.UpdateObject(entity);
        _context.SaveChanges();
    }

    public void Delete(Func<TEntity, bool> predicate)
    {
        TEntity entity = _entitySet.Single(predicate);
        _entitySet.DeleteObject(entity);
        _context.SaveChanges();
    }

    public TEntity Get(Func<TEntity, bool> predicate)
    {
        return _entitySet.SingleOrDefault(predicate);
    }

    public IQueryable<TEntity> Query()
    {
        return _entitySet;
    }
}

我想创建一个AuditableRepository<T>的概念。我应该像这样创建它:

interface IAuditable<T>
interface IRepository<T>
AuditableRepository<T> : IRepository<T>, IAuditable<T>
EFRepository<T> : AuditableRepository<T>

或者这样更好:

interface IAuditable<T>
interface IRepository<T>
EFRepository<T> : IRepository<T>, IAuditable<T>

甚至:

interface IAuditable<T>
interface IRepository<T>
AuditableRepository<T> : IRepository<T>, IAuditable<T>
EFRepository<T> : IRepository<T>
AuditableEFRepository<T> : AuditableRepository<T>

并非所有的EFRepositories都需要审核。我该怎么办?

4 个答案:

答案 0 :(得分:5)

这是另一种可能性(使用Decorator对象向现有存储库添加其他功能):

public sealed class Auditor<T> : IRepository<T>
{
    private readonly IRepository<T> _repository;

    public Auditor(IRepository<T> repository)
    {
        _repository = repository;    
    }

    public void Create(T entity)
    {
        //Auditing here...
        _repository.Create(entity);
    }

    //And so on for other methods...
}

使用Decorator添加其他功能的好处是,它可以避免在您考虑使用审计的某些存储库时开始看到的组合爆炸,有些没有,有些使用EF,有些则没有。每一个可能适用或不适用的新功能都会逐渐变得更糟,通常最终会转移到配置标志和凌乱的内部分支中。

答案 1 :(得分:3)

存储库是否可审核是否重要?这意味着,您是否需要知道存储库是IAuditableRepository还是IRepository?如果没有,您可以使用DI并添加一个IAuditor的构造函数。然后在您的存储库方法中,如果IAuditor可用,则可以使用它。

sealed class EFRepository<TEntity> : Repository<TEntity>
    where TEntity : EntityObject
{
    ObjectContext _context;
    ObjectSet<TEntity> _entitySet;
    IAuditor _auditor;

    public EFRepository(ObjectContext context) : this(context, null)
    {
    }
    public EFRepository(ObjectContext context, IAuditor auditor)
    {
        _context = context;
        _entitySet = _context.CreateObjectSet<TEntity>();
        _auditor = auditor; 
    }
    public override void Create(TEntity entity)
    {
        _entitySet.AddObject(entity);
        _context.SaveChanges();

        if (_auditor != null)
        {
            // audit
        }
    }

    // etc.
}

答案 2 :(得分:2)

感觉不是一个完美的解决方案,因为Dan说你可能会遇到不同界面组合的问题。审计功能听起来并不像它真的需要成为课程的一部分。我认为不是采用Dan的方法,而是在存储库中声明并触发事件。通过这种方式,您可以将许多不同的东西连接起来,更加灵活。因此,请声明Created,Deleted等事件。

将来它还可以通过反应式扩展等功能提供一些非常好的功能。

答案 3 :(得分:2)

首先,如果你的Repository<T>没有任何常用功能(你只有抽象方法而没有实现),我要做的第一件事就是摆脱它。程序的所有其他部分最好只通过IRepository<T>接口访问存储库,这样可以在以后为您提供更大的自由。

第二件事,拥有EFRepository类意味着您希望在某一天留下一个选项以切换到不同的ORM。我的意见是 - 不要这样做。如果您选择EF,请坚持使用EF。如果你真的认为这是一个选项,至少要创建一个带有命名空间MyApp.EntityORM或其他的独立程序集,将所有的Repository类放在那里并删除EF前缀。然后,如果它发生了,您可以在某天通过依赖注入加载正确的ORM,而无需更改代码的其余部分。

根据我的经验:ORM之间可能存在微妙的差异,这会使您无法以真正透明的方式交换它们,并且您几乎不可能需要为单个项目更改ORM。

第二件事,我也更喜欢在不同的类中包装基本存储库的基本功能(像Dan Bryant已经说过的Decorator模式),而不是覆盖已经存在的类。原因是覆盖经常导致不得不处理基类的内部,这有时会变得有点混乱。

从您的问题来看,尚不完全清楚“可审核的存储库”应该如何表现,但如果它只是一个简单的IRepository<T>(它审核IRepository<T>中实现的所有方法),那么你不需要单独的界面:

interface IRepository<T>
class Repository<T> : IRepository<T>
class AuditableRepository<T> : IRepository<T>

除了丹所说的,你可以有一个工厂方法来创建合适的包装器:

class AuditableRepository<T> : IRepository<T>
{
     public static IRepository<T> CreateFrom(IRepository<T> baseRepo)
     { 
          // wrap base repo in an auditable repository
          // and return it
          return new AuditableRepository<T>(baseRepo);
     }
}