2

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

我的目标是最小化我的服务层中方法定义的数量。

这是我的存储库合同:

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)

感谢帮助。

4

2 回答 2

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. 通过正确的实现,您还可以将您的规范链接在一起。

repository.Find(
    firstSpecification
        .And(secondSpecification)
        .Or(thirdSpecification)
        .OrderBy(orderBySpecification));
于 2010-08-25T11:07:52.687 回答
0

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

于 2010-08-25T00:49:46.660 回答