使用存储库/服务层设计模式的建议

时间:2010-08-25 00:39:55

标签: c# linq extension-methods repository-pattern service-layer

尝试在此处创建一个非常简单的存储库和服务层模式。 (.NET 4,C#,LINQ,虽然这个问题部分与语言无关)。注意:这只是R& D。

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

这是我的存储库合同:

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

没有新的东西。

现在,这就是我(服务合同)中的(尝试):

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

基本上,任何特定的“Foo”都有许多属性(id,name等),我希望能够搜索到它们。

所以,我不想为每个不同的属性使用1x Find方法,我只想要一个 - 当我创建额外的属性时,我不必修改合同。

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

所以,这就是我想要做的,这是我的问题:

  • 这是一个糟糕的设计吗?如果是这样,有哪些替代方案?
  • 如何在服务层实施此过滤?我是否必须检查“FooSearchArgs”的哪些属性设置,然后继续过滤? (如果这个,那么query.where,如果这个,query.where等)任何人都知道一个聪明的LINQ IEnumerable扩展方法来做到这一点? (即repository.WhereMeetsSearchCriteria(fooSearchArgs)

感谢帮助。

2 个答案:

答案 0 :(得分: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();
    }
}

在运行查询之后,存储库会对从规范返回的结果IQueryable调用ToArray或ToList,以便在那里评估查询。虽然这似乎不如暴露IQueryable灵活性,但它具有几个优点。

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

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

答案 1 :(得分:0)

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