2010-08-25 82 views
2

试图在这里创建一个非常简单的存储库和服务层模式。 (.NET 4,C#,LINQ,尽管这个问题部分与语言无关)。注意:这只是R & D.存储库/服务层设计模式的建议

我的目标是尽量减少我的服务层中的方法定义的数量。

这里是我的仓库合同:

interface IFooRepository 
{ 
    IEnumerable<Foo> Find(); 
    void Insert(Foo foo); 
    void Update(Foo foo); 
    void Delete(Foo foo); 
} 

没有什么新东西。

现在,这里是IM(试图)有我的服务合同:

interface IFooDataService 
{ 
    public IEnumerable<Foo> Find(FooSearchArgs searchArgs); 
} 

从本质上讲,任何特定的“富”有许多属性(ID,姓名等),我想是能够搜索。

所以,我不想有1x查找方法为每个不同的属性,我只想要一个 - 这种方式时,我创建额外的属性,我不必修改合同。

“FooSearchArgs”只是一个简单的POCO,它具有所有不同的“Foo”属性。

所以,这就是我尝试做的,这是我的问题:

  • 这是糟糕的设计?如果是这样,有什么选择?
  • 我该如何在服务层实现这个过滤?我是否需要检查“FooSearchArgs”的属性设置,然后继续过滤? (如果这样,然后query.where,如果这,query.where等)任何人都有一个聪明的LINQ IEnumerable扩展方法做到这一点的想法? (即repository.WhereMeetsSearchCriteria(fooSearchArgs)

感谢帮助。

回答

3

我们使用的东西非常相似。你需要决定的一件事是如果你打算在仓库之外公开IQueryable。你的find方法返回IEnumerable,它可能是你的when子句返回的IQueryable。

返回IQueryable的好处是您可以进一步优化存储库层以外的标准。

repository.Find(predicate).Where(x => x.SomeValue == 1); 

只有当你使用返回的数据时,表达式才会被编译,这里就是缺点。因为只有在实际使用结果时才打到数据库,最终会在会话(nhibernate)或连接关闭后尝试调用数据库。

我个人的偏好是使用规范模式,在这个模式中你传递你的find方法一个ISpecification对象被用来做查询。

public interface ISpecification<TCandidate> 
{ 
    IQueryable<TCandidate> GetSatisfyingElements(IQueryable<TCandidate> source); 
} 

public class TestSpecification : ISpecification<TestEntity> 
{ 
    public IQueryable<TestEntity> GetSatisfyingElements(IQueryable<TestEntity> source) 
    { 
     return source.Where(x => x.SomeValue == 2); 
    } 
} 

public class ActiveRecordFooRepository: IFooRepository 
{ 
    ... 

    public IEnumerable<TEntity> Find<TEntity>(ISpecification<TEntity> specification) where TEntity : class 
    { 
     ... 

     return specification.GetSatisfyingElements(ActiveRecordLinq.AsQueryable<TEntity>()).ToArray(); 

     ... 
    } 

    public TEntity FindFirst<TEntity>(ISpecification<TEntity> specification) where TEntity : class 
    { 
     return specification.GetSatisfyingElements(ActiveRecordLinq.AsQueryable<TEntity>()).First(); 
    } 
} 

运行查询库后调用的ToArray或ToList对得到的IQueryable从规范返回,以便查询评估那里,然后。虽然这看起来可能不像暴露IQueryable那么灵活,但它具有几个优点。

  1. 查询会立即执行并阻止在会话关闭后调用数据库。
  2. 由于您的查询现在捆绑到规格中,因此它们可以进行单元测试。
  3. 规格是可重复使用的,这意味着您在尝试运行类似查询时没有代码重复,查询中的任何错误只需要在一个地方修复。
  4. 有了正确的实施方式,您还可以将您的规格链接在一起。

repository.Find(
    firstSpecification 
     .And(secondSpecification) 
     .Or(thirdSpecification) 
     .OrderBy(orderBySpecification)); 
+0

一个有趣的事情。不过,我更喜欢使用延迟执行(LINQ),因为它可以更好地控制查询(允许我在服务层中构建它们)。由于使用其他设计模式来解决这个问题(即UnitOfWork),封闭的会话调用并不是问题。虽然(+1)谢谢你的回答 - 我有一个阅读规格模式。 – RPM1984 2010-08-25 11:14:15

+0

您可能会发现这个有趣的:http://huyrua.wordpress.com/2010/07/13/entity-framework-4-poco-repository-and-specification-pattern/ 这是一个如何实现使用规范和工作模式单元的持久性无知存储库。 – Bronumski 2010-08-25 11:30:48

+0

而这一个:http://www.kitchaiyong.net/2009/10/repository-specification-unit-of-work.html。这个更深入一点。 – Bronumski 2010-08-25 11:36:11

0

是否将Func作为参数传递给您的服务层的Find方法,而不是FooSearchArgs选项? Enumerables有一个将Func作为参数的Where方法(linq),因此您可以使用它来过滤结果。

+0

它的一个选项,是的 - 我还在学习表达式树/ lambda表达式。任何机会,你可以用一个例子扩大你的答案(与我的上述问题有关)?谢谢 – RPM1984 2010-08-25 00:56:08

+0

虽然不使用Linq,但这可能效率低下,因为您可能会从数据库返回比需要的更多结果。 – Craig 2010-08-25 01:02:35

+0

@Craig即时通讯使用LINQ。目前只有一个虚假的存储库,但真正的存储库是L2SQL或EF。 – RPM1984 2010-08-25 01:26:04