将查询逻辑封装在EF Core中

问题描述 投票:0回答:1

最近,我看到一篇关于 EF Core 中查询封装的博客文章。这种方法与我更熟悉的存储库模式有些不同。

在存储库模式中,我遵循这样的方法,

public interface ISomeRepo
{
    IEnumerable<string> GetAll();
    // Some other queries
}

public class SomeRepo : ISomeRepo
{
    public IEnumerable<string> GetAll()
    {
        // Get data from database
    }
    // Implementation of other queries
}

但是博客文章中描述的方法是这样的,

public interface IModelQuery<out TResult, in TModel>
{
    IEnumerable<TResult> Execute(TModel model);
}

public class PartialMatchQuery : IModelQuery<string, IEnumerable<string>>
{
    private readonly string partial;
 
    public PartialMatchQuery(string partialString)
    {
        partial = partialString;
    }
 
    public IEnumerable<string> Execute(IEnumerable<string> model)
    {
        return model.Where(s => s.ToLower().Contains(partial));
    }
}

并通过这种方式查询数据源,

static int Main(string[] args)
{
    var strings = new List<string>() // I used an IEnumerable<T> implemented collection for simplicity
            {
                "Abhc", "Bmhh", "Csjudsm", "Dsjuh", "Ejhduhb", "Fmjasgh"
            };

    var query = new PartialMatchQuery("m");
    var result = query.Execute(strings).ToList();

    return 0;
}

我觉得这种“新”方法与包含一组“严格”操作(如

GetAll()
GetById()
Modify()
等)的存储库模式相比是灵活的。另一方面,有了这个
IModelQuery
的东西,如果我有 100 个不同的查询,我最终可能会得到 100 个类。我在网上搜索了这种新方法的一些实际实现(至少对我来说是新的),但找不到任何。

所以,这是我的问题:

  1. 是否真的值得使用这种新方法而不是承担“每个查询一个类”成本的存储库模式?
  2. 使用这种新方法是否有任何副作用(我错过了),会影响可维护性或可扩展性?
c# entity-framework-core repository-pattern encapsulation
1个回答
0
投票

我阅读了由两部分组成的博客,只有第二部分介绍了如何使用 EF 来实现这一点。老实说,我所看到的只是一堆名副其实的垃圾包装代码,这些代码可以通过使用

IQueryable
来完成。

在微服务世界中,当操作范围缩小到单个实体时,像这样的模式和 CQRS 可能很有用。对于大多数业务应用程序来说,深入研究这样的内容将非常昂贵且受到限制。

它将需要更多的代码来利用异步操作,并且他的测试代码实现将不适用于

async
操作。这也不适用于投影,也不适用于处理更复杂的实体结构时急切加载依赖项之类的事情。简而言之,这是一种徒劳的练习。您在博客上找到的大多数示例仅涵盖极其简单的用例,但您很快就会发现在现实世界的解决方案中存在巨大的差距,需要更多的代码来尝试和解决,更加复杂,偏离“模式” ”,或者最终得出“EF 太慢”的结论,因为将其硬塞到某种抽象中,只会成功地有效地削弱它。

EF 不需要存储库模式,它在 DbSet

 类中有一个。它不需要工作单元模式,因为它在 
DbContext
 类中有一个。这并不是说您不能在其上提取工作单元或存储库模式,但您需要小心抽象的原因并选择不会削弱 EF 功能的实现。

阅读那篇文章后我得到的建议是什么?躲开它。它需要更多的工作来处理异步操作,它不容易适应投影(

Select

 / 
ProjectTo
等),并且不适应急切加载(
Include
)或没有更多方法、参数和/的分页之类的东西或复杂性。

出于同样的原因,绝对避免使用

IEnumerable<TEntity> GetAll()

 等通用存储库模式。

对于大多数项目,尤其是那些刚开始使用 EF 进行编写的开发人员,只需使用

DbContext

DbSet
 并学习依靠投影来进行读取操作,并提前加载更新。避免诸如分离实体之类的事情,包括在服务层之间传递实体,使用投影的 DTO 或 ViewModel。如果您后来开发到想要利用单元测试的程度,或者正在开发像多租户 SaaS 之类的系统或具有软删除实现的系统,那么您不想依赖 EF 的核心过滤规则来进行软删除。删除并拥有核心域规则,那么我可以推荐一个存储库模式,但利用 
IQueryable<TEntity>
 而不是直接返回 
IEnumerable<TEntity>
 甚至 
TEntity
。这种类型的存储库可以轻松模拟单元测试,并且可以封装核心域规则,同时保持完全灵活地处理 
async
 操作、投影、急切加载、分页、过滤和排序。

如果开发微服务解决方案,将事情分解为完全独立工作并且可以与观察者等相关的个性化命令或查询是有意义的,那么就努力尝试这样的事情。 ;)

© www.soinside.com 2019 - 2024. All rights reserved.