我们正在开发 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();
}

每个存储库可以实现此接口,例如︰

  • 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 与否) 是否完全包含在存储库中实现,无论谁实现服务只需要知道要调用哪个存储库函数。

我想我看不到在服务类中编写所有表达式语法和传递到存储库的优势。这岂不意味着容易 messup LINQ 代码在很多情况下被复制?

例如,在我们旧的开票系统,我们称之为

InvoiceRepository.GetSingleInvoice(DateTime invoiceDate, int accountId)

从几个不同的服务 (客户、 发票、 帐户等)。这种方法似乎更清楚比编写以下多个位置中︰

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

我看到使用特定方法的唯一缺点是,我们最终可能会得到许多屏蔽的 Get * 函数,但这仍然不到服务类推的表达式逻辑更可取。

我缺少什么?

2009-08-05 00:18:19
问题评论:

回答:

这是存储库模式本身一样旧问题。最近出现的 LINQ 的IQueryable,统一的查询表示形式,导致了大量有关这一主题的讨论。

我更喜欢特定资料库自己,努力工作来构建一个通用存储库框架之后。无论我尝试过什么聪明的机制,我总是最终在同一个问题︰ 存储库是属于要建模域和该域不是泛型。不是每个实体可以将其删除,可以添加不是每个实体,不是每个实体都有一个存储库。查询不同大差异;知识库 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);
    }
}

替换为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 的存储库中不允许用户删除。此外,公开IQueryable是一整套不同的可能的蠕虫-多个观点为 belly 按钮上有该主题。

老实说,卓越回答。谢谢 !

那么,如何使用 IoC/DI 与此?(我是初学者在 IoC)我在完全模式方面的问题︰ stackoverflow.com/questions/4312388/...

"存储库是属于要建模域和该域不是泛型。不是每个实体可以将其删除,可以添加不是每个实体,不是每个实体都有一个存储库"完美 !

我知道这是旧的答案,但我很好奇,如果从存储库中类与有意留出一个更新方法。我找不到清晰的方式来执行此操作。

@Tanner︰ 更新都是隐式-当您修改跟踪的对象,然后提交DataContext,LINQ to SQL 将发出相应的命令。

我实际上不同意稍微王志的开机自检。我认为他是对的最终一切都已非常独特,等等。但在同一时间,大部分的脱落设计,并找到启动一个通用存储库并使用它开发我的模型时,我可以得到了速度非常快,然后重构在发现需要这样做的更多特定于应用程序。

因此,在这样的情况下,我经常创建通用的 IRepository 具有完全 CRUD 堆栈上,并且,让我快速进入播放与 API,让朋友们播放带有用户界面并进行集成和用户验收测试并行。然后,我发现我需要 repo 等特定查询,我开始替换此依赖项的一个带,如果需要将从那里。一个基础的实现。可以很容易地创建和使用 (并可能是内存中数据库或静态对象模拟对象或任何挂钩)。

尽管如此,什么我已开始做最近正在分解行为。因此,如果您对 IDataFetcher 进行操作接口,IDataUpdater、 IDataInserter 和 IDataDeleter (例如) 您可以混合和匹配来定义您的要求通过界面,然后小心的部分或全部用户,实现,我仍然可以插入时我正在构建出该应用程序使用的执行 it 全部实现。

保罗

感谢您的答复 @Paul。我确实尝试过这种方法也。我无法弄清楚如何一般性地表达我尝试过,第一次方法GetById()应使用IRepository<T, TId>GetById(object id),或做出假设和使用GetById(int id)复合键将如何工作?我想知道按 ID 一般选择是否物有所值的抽象。如果不是这样,还有什么会一般存储库被强制到明示 akwardly?这是抽象实现,没有接口背后原因的行.

此外,通用查询机制是 ORM 的责任。您的存储库应使用通用查询机制的实施项目的实体的特定查询。您的存储库的使用者不应该被迫编写自己的查询,除非这是属于您的问题域,如报告。

@Bryan-关于您的 GetById()。我使用 FindById < T,TId >(TId id);从而导致类似于存储库。FindById < 发票,int > (435);

我不通常置于字段级查询方法的泛型接口,坦白地说。随着曾指出,并不是所有的模型应查询单个密钥,并且在某些情况下您的应用程序将不会再获得按 ID 的内容根本 (例如如果您正在使用数据库生成的主键,只读取通过自然键,例如登录名)。我作为重构的一部分生成的特定接口上演化的查询方法。

与重写的方法的签名,我喜欢从通用存储库 (或一般存储库指定确切的行为列表) 派生的特定资料库。

可能请提供一个简单的 snippet-ish 示例?

@Johann Gerell 不能,因为我不再使用的存储库。

现在,您从存储库中保持使用什么?

@Chris i 侧重点有富域模型。输入应用程序的一部分是什么很重要。如果仔细监控所有状态更改,也没有关系,得如何,只要它是效率足够高、 可能读取数据。因此,我只是直接使用 NHibernate ISession。存储库一层抽象,而是指定东西喜欢预先加载、 多查询等,如果真的需要不难太模拟出 ISession 方式更容易。

@JesseWebb Naah...富域获取大大简化模型的查询逻辑。例如如果我想要搜索的用户购买东西,我只是搜索的用户。其中 (u = > u.HasPurchasedAnything) 而不是用户。加入 (x = > 订单,是某件事情,我不知道 linq)。位置 (顺序 = > 顺序。状态 = = 1)。加入 (x = > x.products)。其中 (x...等等等等等等...blah 但

Greg Young 关于此主题有很好的帖子。http://codebetter.com/gregyoung/2009/01/16/ddd-the-generic-repository/

有的特定存储库包装通用存储库。通过这种方式可以控制公共接口,但仍有来自的代码重用的好处有一个通用的存储库。

请输入您的翻译

Advantage of creating a generic repository vs. specific repository for each object?

确认取消