猿问
下载APP

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

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

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

例如:一个通用的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();}

每个Repository都会实现此接口,例如:

  • 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 *函数的排列,但这仍然似乎比将表达式逻辑推送到Service类更好。

我错过了什么?


梵蒂冈之花
浏览 48回答 3
3回答

慕雪6173905

这是一个与Repository模式本身一样古老的问题。最近引入的LINQ&nbsp;IQueryable是查询的统一表示,引发了很多关于这个主题的讨论。在努力构建通用存储库框架之后,我自己更喜欢特定的存储库。无论我尝试过哪种聪明的机制,我总是遇到同样的问题:存储库是被建模的域的一部分,并且该域不是通用的。并非每个实体都可以删除,不是每个实体都可以添加,并非每个实体都有一个存储库。查询变化很大;&nbsp;存储库API变得与实体本身一样唯一。我经常使用的模式是具有特定的存储库接口,但是实现的基类。例如,使用LINQ to SQL,您可以:public&nbsp;abstract&nbsp;class&nbsp;Repository<TEntity>{ &nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;DataContext&nbsp;_dataContext; &nbsp;&nbsp;&nbsp;&nbsp;protected&nbsp;Repository(DataContext&nbsp;dataContext) &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_dataContext&nbsp;=&nbsp;dataContext; &nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;protected&nbsp;IQueryable<TEntity>&nbsp;Query &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;get&nbsp;{&nbsp;return&nbsp;_dataContext.GetTable<TEntity>();&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;protected&nbsp;void&nbsp;InsertOnCommit(TEntity&nbsp;entity) &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_dataContext.GetTable<TEntity>().InsertOnCommit(entity); &nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;protected&nbsp;void&nbsp;DeleteOnCommit(TEntity&nbsp;entity) &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_dataContext.GetTable<TEntity>().DeleteOnCommit(entity); &nbsp;&nbsp;&nbsp;&nbsp;}}替换DataContext为您选择的工作单元。示例实现可能是:public&nbsp;interface&nbsp;IUserRepository{ &nbsp;&nbsp;&nbsp;&nbsp;User&nbsp;GetById(int&nbsp;id); &nbsp;&nbsp;&nbsp;&nbsp;IQueryable<User>&nbsp;GetLockedOutUsers(); &nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;Insert(User&nbsp;user);}public&nbsp;class&nbsp;UserRepository&nbsp;:&nbsp;Repository<User>,&nbsp;IUserRepository{ &nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;UserRepository(DataContext&nbsp;dataContext)&nbsp;:&nbsp;base(dataContext) &nbsp;&nbsp;&nbsp;&nbsp;{} &nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;User&nbsp;GetById(int&nbsp;id) &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;Query.Where(user&nbsp;=>&nbsp;user.Id&nbsp;==&nbsp;id).SingleOrDefault(); &nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;IQueryable<User>&nbsp;GetLockedOutUsers() &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;Query.Where(user&nbsp;=>&nbsp;user.IsLockedOut); &nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;Insert(User&nbsp;user) &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;InsertOnCommit(user); &nbsp;&nbsp;&nbsp;&nbsp;}}请注意,存储库的公共API不允许删除用户。此外,暴露IQueryable是另一种蠕虫 - 在该主题上有与肚脐一样多的意见。

杨魅力

public class UserRepository:Repository,IUserRepository您不应该注入IUserRepository以避免暴露接口。正如人们所说,你可能不需要完整的CRUD堆栈等。

慕姐4208626

我实际上与布莱恩的帖子略有不同意见。我认为他是对的,最终一切都非常独特等等。但与此同时,大部分内容都是在你设计的时候出现的,而且我发现在开发我的模型的同时获得一个通用的存储库并使用它,我可以很快得到一个应用程序,然后重新调整到更高的特异性,因为我发现需要这样做。因此,在这种情况下,我经常创建一个具有完整CRUD堆栈的通用IRepository,这使我能够快速使用API并让人们玩UI并同时进行集成和用户验收测试。然后,当我发现我需要对repo等进行特定查询时,如果需要,我会从那里开始替换那个特定的依赖关系。一个潜在的影响。很容易创建和使用(可能挂钩到内存中的数据库或静态对象或模拟对象或其他)。那就是说,我最近开始做的就是打破这种行为。因此,如果您为IDataFetcher,IDataUpdater,IDataInserter和IDataDeleter(例如)执行接口,您可以通过接口混合并匹配来定义您的需求,然后实现处理其中的部分或全部,并且我可以我正在构建应用程序时仍然注入使用它的所有实现。
打开App,查看更多内容
随时随地看视频慕课网APP
我要回答