为每个对象创建通用存储库与特定存储库的优势?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (16)

我们正在开发ASP.NETMVC应用程序,现在正在构建存储库/服务类。我想知道创建一个通用的IRepository接口是否有任何主要的优势,所有的存储库都可以实现,而每个存储库都有自己独特的接口和一组方法。

public interface IRepository : IDisposable
{
    T[] GetAll<T>();
    T[] GetAll<T>(Expression<Func<T, bool>> filter);
    T GetSingle<T>(Expression<Func<T, bool>> filter);
    T GetSingle<T>(Expression<Func<T, bool>> filter, List<Expression<Func<T, object>>> subSelectors);
    void Delete<T>(T entity);
    void Add<T>(T entity);
    int SaveChanges();
    DbTransaction BeginTransaction();
}

每个存储库将实现这个接口,例如:

  • CustomerRepository:IRepository
  • ProductRepository:IRepository
  • 等等。

我们在以前的项目中所遵循的替代方案是:

public interface IInvoiceRepository : IDisposable
{
    EntityCollection<InvoiceEntity> GetAllInvoices(int accountId);
    EntityCollection<InvoiceEntity> GetAllInvoices(DateTime theDate);
    InvoiceEntity GetSingleInvoice(int id, bool doFetchRelated);
    InvoiceEntity GetSingleInvoice(DateTime invoiceDate, int accountId); //unique
    InvoiceEntity CreateInvoice();
    InvoiceLineEntity CreateInvoiceLine();
    void SaveChanges(InvoiceEntity); //handles inserts or updates
    void DeleteInvoice(InvoiceEntity);
    void DeleteInvoiceLine(InvoiceLineEntity);
}

在第二种情况下,表达式(LINQ或其他)将完全包含在Repository实现中,无论是谁实现服务,只要知道要调用哪个存储库函数即可。

我想我没有看到在服务类中编写所有表达式语法并传递到存储库的好处。这不意味着在许多情况下容易被复制LINQ代码吗?

例如,在我们以前的发票系统中,我们调用

InvoiceRepository.GetSingleInvoice(DateTime invoiceDate, int accountId)

从几个不同的服务(客户,发票,帐户等)。这似乎比在多个地方编写以下内容要干净得多:

rep.GetSingle(x => x.AccountId = someId && x.InvoiceDate = someDate.Date);

我发现使用特定方法的唯一缺点是,我们最终可能会得到许多GET的排列。函数,但这似乎仍然比将表达式逻辑向上推到服务类中更好。

我遗漏了什么?

提问于
用户回答回答于

LINQ的近况IQueryable一个查询的统一表示,引起了很多关于这个主题的讨论。

我自己更喜欢特定的存储库,因为我已经非常努力地构建了一个通用的存储库框架。不管我尝试了什么聪明的机制,我总是会遇到同样的问题:存储库是正在建模的域的一部分,而且该域不是通用的。不是每个实体都可以被删除,不是每个实体都可以被添加,也不是每个实体都有一个存储库。查询变化很大;存储库API变得和实体本身一样独特。

我经常使用的一种模式是拥有特定的存储库接口,但它是实现的基类。例如,使用LINQtoSQL,您可以:

public abstract class Repository<TEntity>
{
    private DataContext _dataContext;

    protected Repository(DataContext dataContext)
    {
        _dataContext = dataContext;
    }

    protected IQueryable<TEntity> Query
    {
        get { return _dataContext.GetTable<TEntity>(); }
    }

    protected void InsertOnCommit(TEntity entity)
    {
        _dataContext.GetTable<TEntity>().InsertOnCommit(entity);
    }

    protected void DeleteOnCommit(TEntity entity)
    {
        _dataContext.GetTable<TEntity>().DeleteOnCommit(entity);
    }
}

取代DataContext你选择的工作单位。一个实例实现可以是:

public interface IUserRepository
{
    User GetById(int id);

    IQueryable<User> GetLockedOutUsers();

    void Insert(User user);
}

public class UserRepository : Repository<User>, IUserRepository
{
    public UserRepository(DataContext dataContext) : base(dataContext)
    {}

    public User GetById(int id)
    {
        return Query.Where(user => user.Id == id).SingleOrDefault();
    }

    public IQueryable<User> GetLockedOutUsers()
    {
        return Query.Where(user => user.IsLockedOut);
    }

    public void Insert(User user)
    {
        InsertOnCommit(user);
    }
}

注意,存储库的公共API不允许用户被删除。

用户回答回答于

但是与此同时,大部分的内容都是在你设计的时候出现的,我发现在开发我的模型时,建立一个通用的存储库并使用它,我可以非常快地得到一个应用程序,然后重构到更大的特异性,因为我发现有必要这样做。

因此,在这种情况下,我经常创建一个具有完整CRUD堆栈的通用IRepository,它使我能够快速地使用API,让人们玩w/UI,并行地进行集成和用户接受测试。然后,当我发现我需要关于回购等的特定查询时,我开始替换那个依赖关系w/如果需要的话,然后从那里开始。一个底层的实现很容易创建和使用(可能还可以挂钩到内存中的数据库或静态对象或模拟对象或其他任何东西)。

尽管如此,我最近开始做的是打破这种行为。因此,如果您为IDataFetcher、IDataUpdater、IDataInserter和IDataDeleter(例如)做了接口,可以通过接口混合和匹配来定义您的需求,然后有一些或所有的实现来处理它们,我仍然可以在构建应用程序时注入do-it-所有的实现。

扫码关注云+社区