2010-11-28 65 views
2

我正在将一些公共存储库基础结构合并到某些包装EF,L2SQL和WCF数据服务的应用程序中(尽管基础数据访问实现应该是任意的)。我已经对此事进行了一些阅读,但似乎无法找到真正满足的例子。通用存储库

我开始:

public interface IRepository : IDisposable 
{ 
    IQueryable<T> Query<T>(); 
    void Attach(object entity); 
    void ForDeletion(object entity); 
    void SaveChanges(); 
} 

但我喜欢用窄合同(http://codebetter.com/blogs/gregyoung/archive/2009/01/16/ddd-the-generic-repository.aspx)存储库的想法。以上内容让消费者知道存储库支持的所有实体类型。

我不会说这是不可能的,但我很难确信IQueryables本身不应该是存储库合同的一部分。我不是锅炉板代码的粉丝,我坚信你拥有的越多,引入的维护黑洞就越多。所以,我要说的是,这将是很难说服我,任何看起来像:

Public IEnumerable<Customer> GetCustomersWithFirstNameOf(string _Name) { 
    internalGenericRepository.FetchByQueryObject(new CustomerFirstNameOfQuery(_Name)); //could be hql or whatever 
} 

是什么,但一个完全糟糕想法。当你想要搜索名字姓氏时怎么办?或者名字姓氏等等。您最终将拥有一个拥有超过1000个操作的存储库,其中一半重复相同的逻辑。注:我不是调用代码应该是负责应用的所有过滤和这样的,而是具有互补的规范源对我来说很有意义:

public static class CustomerSpecifications 
{ 
    public IQueryable<Customer> WithActiveSubscriptions(this IQueryable<Customer> customers, DateTime? start, DateTime? end) 
    { 
     // expression manipulation 
     return customers; 
    } 
} 


// bind some data source 
repository.GetQueryable().WithActiveSubscriptions(); 

好了,往前走,我认为有域模型明确仓库听起来像一个好主意,按以下格式:

public interface IRepository : IDisposable 
{ 
    void SaveChanges(); 
} 

public interface IRepository<T>: IRepository 
{ 
    IQueryable<T> GetQueryable(); 
    void Attach(T entity); 
    void ForDeletion(T entity); 
} 

然后

public class CustomerRepository:IRepository<Customer> 
{ 
    private ObjectContext _context; 
    // trivial implementation 
} 

,但我的这个问题是,它只是让我DELET e客户。那么我想删除客户地址的情况呢?也就是说,我使用存储库来查询客户实体,但是然后想要删除myCustomer.CustomerAddresses [0]?我需要创建第二个存储库来简单地附加和删除我想要的地址?

我想我可以有我的CustomerRepository是:

public class CustomerRepository:IRepository<Customer>, IRepository<CustomerAddress> 
{ 
    private ObjectContext _context; 
    // trivial implementation 
} 

这将让我重新使用存储库中删除CustomerAddresses,但我不知道我的感觉如何继承IRepository<T>对于图形的每一个部分我想要公开删除...

public class CustomerRepository:IRepository<Customer>, IRepository<CustomerAddress> /* this list may get pretty long, and then I really just have a masked EF ObjectContext, don't I? */ 
{ 

任何人都有更好的实施建议吗?

回答

2

在那里有很多问题,其中一些答案可能是主观的/被挑选出来的,但我会随着拳击而滚动。

IQueryable vs特定的方法。

我其实同意你的看法。我不喜欢有很多很多方法来定义我的存储库上的所有不同操作。我更喜欢允许调用代码提供规范。现在,我的调用代码不是我的演示文稿 - 它是一个域服务/ BLL。因此,我们并没有为UI充分发挥作用,而只是让我们的存储库保持非常通用。 DDD纯粹主义者认为,存储库是您的域模型的抽象,因此应该服务于特定的域操作。我会留下来让你决定。此外,如果您使用IQueryable,那么您将强制使用“未知”LINQ提供程序。对我来说不是一个问题,因为我总是会使用LINQ(例如Entity Framework)的东西。但这是DDD纯化论者所面临的另一个问题。

,我用Expression<Func<T,bool>>我的域名服务,当你要在姓和名

那么搜索什么,所以我能做到这一点:

var customers = repository.FindAll<Customer>(x => x.FirstName == "Bob" && x.LastName == "Saget"); 

如果您不喜欢,你可以使用规范模式来使用AND/OR类型的代码。

删除客户地址

那么一般来说,你应该有每个聚合根一个存储库。在不知道域模型的情况下,它听起来像“客户”是一个聚合根,“CustomerAddress”是一个总是与“客户”关联的聚合(例如不存在没有合适的客户)。

因此,您不需要CustomerAddressRepository。 CustomerAddress操作应通过您的CustomerAddress存储库完成。

这就是为什么我喜欢营造一种GenericRepository<T>实施IRepository<T>其对总(查找,添加,删除),然后更具体实现从GenericRepository<T>派生实现核心业务。

下面是我将如何处理客户信息库。创建一个GenericRepository<T>,它执行IRepository<T>核心操作。然后为ICustomerRepository创建另一个接口,该接口也实现IRepository<T>

现在,创建一个名为CustomerRepository的实现,它继承GenericRepository<T>的核心存储库实现,并且还扩展了这个实现以允许额外的操作(如删除地址)。

public class CustomerRepository : GenericRepository<Customer>, ICustomerRepository 
{ 
    // inherits, for example: 
    // IQueryable<Customer> Query<Customer>(); 

    public void Remove(Address address) 
    { 
     // get the customer (if you havent already got it) 
     var cust = ctx.Customers.SingleOrDefault(x => x.CustId == address.CustId); 
     cust.Addresses.Remove(address); 
    } 
} 

和你ICustomerRepository应该是这样的:

public interface ICustomerRepository : GenericRepository<Customer>, IRepository<Customer> 
{ 
    // inherited from GenericRepository<Customer> 
    //IQueryable<T> Query<T>(); 
    //void Attach(object entity); 
    //void ForDeletion(object entity); 
    //void SaveChanges(); 
    void Remove(Address address); 
} 

明白我的意思?从真正通用的存储库开始,然后根据需要获取更具体的信息。

您不需要CustomerAddressRepository。如上所示,通过CustomerRepository执行操作。

HTH。

+0

因此,您对每个聚合根拥有GenericRepository ...因为在CustomerAddress中没有GenericRepository? – Jeff 2010-11-29 00:52:29