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

发布于 2024-09-16 00:56:08 字数 938 浏览 4 评论 0原文

尝试在这里创建一个非常简单的存储库和服务层模式。 (.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、名称等),我希望能够搜索这些属性。

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

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

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

  • 这是糟糕的设计吗?如果是这样,有哪些替代方案?
  • 如何在服务层实现这种过滤?我是否必须检查设置了“FooSearchArgs”的哪些属性,然后继续过滤? (if this, then query.where,if this,query.where,等等)有人知道一个聪明的 LINQ IEnumerable 扩展方法来执行此操作吗? (即repository.WhereMeetsSearchCriteria(fooSearchArgs)

感谢您的帮助。

Trying to make a really simple repository and service layer pattern here. (.NET 4, C#, LINQ, although this question is partially language-agnostic). Note: this is just R&D.

My goal is to minimize the amount of method definitions in my service layer.

Here's my Repository Contract:

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

Nothing new there.

Now, here's what im (trying) to have in my Service Contract:

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

Essentially, any particular "Foo" has many properties (id, name, etc), which i would like to be able to search upon.

So, i dont want to have 1x Find method for each different property, i just want one - that way when i create extra properties i dont have to modify the contracts.

The "FooSearchArgs" is just a simple POCO with all the different "Foo" properties it.

So, that's what im trying to do, here's my questions:

  • Is this poor design? If so, what are the alternatives?
  • How can i implement this filtering in the service layer? Would i have to check what properties of "FooSearchArgs" are set, then keep filtering down? (if this, then query.where, if this, query.where, etc) Anyone have an idea of a clever LINQ IEnumerable extension method to do this? (ie repository.WhereMeetsSearchCriteria(fooSearchArgs))

Appreciate the help.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

垂暮老矣 2024-09-23 00:56:08

我们使用非常相似的东西。您需要决定的一件事是是否要在存储库外部公开 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));

We use something very similar. One thing you need to decide on is if you are going to expose IQueryable outside of the repository. Your find method returns IEnumerable which could be the IQueryable returned from your when clause.

The advantage of returning the IQueryable is that you can further refine your criteria up outside of your repository layer.

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

The expression will only be compiled when you come to use the returned data and here in lies the disadvantage. Because you only hit the database when you actually come to use the results you could end up trying to call the database after your session (nhibernate) or connections have been closed.

My personal preference is to use the specification pattern where you pass your find method an ISpecification object is used to do the query.

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();
    }
}

After the query is run the repository calls ToArray or ToList on the resulting IQueryable returned from the specification so that the query is evaluated there and then. Whilst this may seem less flexible than exposing IQueryable it comes with several advantages.

  1. Queries are executed straight away and prevents a call to the database being made after sessions have closed.
  2. Because your queries are now bundled into specifications they are unit testable.
  3. Specifications are reusable meaning you don't have code duplication when trying to run similar queries and any bugs in the queries only need to be fixed in one place.
  4. With the right kind of implementation you can also chain your specifications together.

repository.Find(
    firstSpecification
        .And(secondSpecification)
        .Or(thirdSpecification)
        .OrderBy(orderBySpecification));
焚却相思 2024-09-23 00:56:08

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

Is passing a Func as a parameter to your service layer's Find method, instead of the FooSearchArgs, an option? Enumerables have a Where method (linq) that takes a Func as a parameter, so you could use it to filter the results.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文