创建存储库而不使用 IQueryable 进行规范

发布于 2024-10-25 08:32:06 字数 1463 浏览 3 评论 0原文

我最近一直在研究 DDD、存储库和规范模式,在阅读了大量博客和示例后,我试图提出一个我满意的存储库。

直到最近,我一直在我的存储库上公开 IQueryable,但在了解 IQueryable 是一个有漏洞的抽象之后,因为它是延迟执行的,并且实际上跨越了我的数据层的边界,我对其进行了更改,以便我的存储库返回 IEnumerable。

例如,我可能有这样的事情:

public interface IUserRepository
{
    IEnumerable<User> All();

    void Save(User item);
    void Delete(User item);
}

我认为好吧,这看起来不错,但如果我想过滤我的名字或电子邮件数据怎么办?阅读一篇博客文章后,我实现了一种将 ICriteria 传递到 All() 方法的方法。

public IEnumerable<TEntity> All(ICriteria<TEntity> criteria)
{
    return criteria.BuildQueryFrom(Set).ToList();
    // Set is a DbSet from EntityFramework
}

还有一个示例标准类:

public class AccountById : ICriteria<Account>
{
    private readonly int _id;

    public AccountById(int id)
    {
        _id = id;
    }

    IQueryable<Account> ICriteria<Account>.BuildQueryFrom(DbSet<Account> dbSet)
    {
        return from entity in dbSet
               where entity.Id == _id
               select entity;
    }
}

这很好用,我可以构建这些标准类来满足我的要求并将它们传递到存储库中,一切都运行良好。

我不喜欢的一件事是与 IQueryable 绑定在一起,因为我必须使用支持 Linq 的 ORM,所以如果我想在存储库中使用 SqlCommand 来提高性能,或者这样我就可以编写更干净的 SQL,而不是 ORM 生成的 SQL ,我该怎么做呢?

我还想避免为每个过滤器编写新方法,如 FindById、FindByUsername、FindByEmail 等。

我将如何创建一个存储库,允许我在不使用 IQueryable 的情况下指定我想要选择的条件,这样它仍然可以工作我使用的是 EF、nHibernate 还是只是普通的 SqlCommand?我正在努力寻找一个使用 SqlCommand 和规范模式的示例。

在 ORM 出现之前人们是如何做到这一点的?

I've recently been looking into DDD, repositories and the specification pattern and after reading a hand full of blogs and examples I'm trying to come up with a repository that I'm happy with.

I have been exposing IQueryable on my repositories until recently but after understanding that IQueryable is a leaky abstraction because of it is deferred execution and is effectively crossing the boundry from my data layer I have changed it so that my repositories return IEnumerable instead.

So I might have something like this for example:

public interface IUserRepository
{
    IEnumerable<User> All();

    void Save(User item);
    void Delete(User item);
}

I thought okay that seems good but what if I wanted to filter the data my firstname or email? After reading a blog post I implemented a way of passing ICriteria into the All() method.

public IEnumerable<TEntity> All(ICriteria<TEntity> criteria)
{
    return criteria.BuildQueryFrom(Set).ToList();
    // Set is a DbSet from EntityFramework
}

And an example criteria class:

public class AccountById : ICriteria<Account>
{
    private readonly int _id;

    public AccountById(int id)
    {
        _id = id;
    }

    IQueryable<Account> ICriteria<Account>.BuildQueryFrom(DbSet<Account> dbSet)
    {
        return from entity in dbSet
               where entity.Id == _id
               select entity;
    }
}

This works fine and I can build these criteria classes to meet my requirements and pass them into the repos and all works well.

One thing I don't like though is being tied to IQueryable because I have to use an ORM that supports Linq so if I wanted to use SqlCommand in my repository for say performance sake or so I can write cleaner SQL rather than the ORM generated SQL, how would I go about doing that?

I would also like to avoid having to write a new method for each filter like FindById, FindByUsername, FindByEmail etc.

How would I go about creating a repository that allows me to specifiy the criteria I want to select without using IQueryable so it would still work whether I used EF, nHibernate or just plain SqlCommand? I'm stuggling to find an example that uses SqlCommand and the specification pattern.

How did people used to do it before ORMs?

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

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

发布评论

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

评论(1

长安忆 2024-11-01 08:32:06

就我个人而言,我不介意 IQueryable 是一个有漏洞的抽象,因为它允许我在服务层中编写 LINQ 查询,因此拥有更多可测试的代码。只要实现 IQueryable 的对象保留在服务层内(即不将它们返回到表示层),我就没有看到问题。它最大限度地提高了应用程序的可测试性。例如,请参阅我关于可测试 LINQified 存储库的博客文章

Personally, I don't mind IQueryable being a leaky abstraction, because it allows me to write LINQ queries in my service layer and and therefore have more testable code. As long as objects that implement IQueryable are kept inside the service layer (i.e. don't return them to the presentation layer) I don't see a problem. It maximizes the testability of your application. See for instance my blog post about testable LINQified repositories.

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