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

例如:通用IRepository接口可能如下所示(取自this answer):

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或其他)将完全包含在存储库实现中,实现服务的人只需要知道调用哪个存储库函数.

我想我看不出在服务类中编写所有表达式语法并将其传递到存储库有什么好处.这不是意味着在很多情况下容易安装的LINQ代码正在被复制吗?

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

InvoiceRepository.GetSingleInvoice(DateTime invoiceDate, int accountId)

来自几个不同的服务(客户、发票、账户等).这似乎比在多个地方写下以下内容要干净得多:

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

我认为使用特定方法的唯一缺点是,我们最终可能会得到许多Get*函数的排列,但这似乎比将表达式逻辑向上推到服务类中更可取.

我遗漏了什么?

推荐答案

这是一个与存储库模式本身一样古老的问题.LINQ‘sIQueryable是查询的统一表示形式,最近的推出引起了很多关于这个话题的讨论.

我自己更喜欢特定的存储库,因为我非常努力地构建了一个通用的存储库框架.不管我try 了什么聪明的机制,我总是遇到相同的问题:存储库是正在建模的域的一部分,而该域不是通用的.不是每个实体都可以删除,也不是每个实体都可以添加,也不是每个实体都有存储库.查询千差万别;存储库API变得与实体本身一样独特.

我经常使用的一种模式是具有特定的存储库接口,但有一个用于实现的基类.例如,使用LINQ to SQL,您可以执行以下操作:

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);
    }
}

用你 Select 的工作单元替换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不允许删除用户.此外,《expose IQueryable人》是另一整罐蠕虫——在这个话题上有很多观点.

Asp.net相关问答推荐

实体框架核心:DBCommand执行得更快,但总时间要慢10倍

有人可以解释一下这个Eager 加载示例吗?

我可以将图像添加到 ASP.NET 按钮吗?

如何使用 executeReader() 方法仅检索一个单元格的值

如何在 GridView 单元格中的(即
)中呈现解码的 HTML

在 EF4.1 中正确地从上下文中附加和分离实体

取消选中时 ASP.NET CheckBox 不会触发 CheckedChanged 事件

多个 DataContext 类是否合适?

如何在txt文件中保存异常?

HttpContext.Current.Request.IsAuthenticated 和 HttpContext.Current.User.Identity.IsAuthenticated 有什么区别?

System.Web.HttpContext 无法识别

ASP.NET 日期时间 Select 器

您如何以编程方式填写表格并发布网页?

如何在新实现的接口或基类之间做出决定?

如何在asp.net中获取应用程序路径?

HttpContext.Current 在 MVC 4 项目中未解决

Html.RenderAction 和 Html.Action 的区别

如何将我的 DDD 模型中的用户与身份验证用户集成?

ASP.NET MVC 和 httpRuntime executionTimeout

如何将 GridView.DataSource 导出到数据表或数据集?