如何通用地创建从泛型类型继承的存储库?

2024-01-23

我目前正在尝试实施存储库模式在我的 DbContext 之上。问题是,我最终遇到了一种情况,我必须将多个存储库注入到一个UnitOfWork构造函数,像这样:

public class UnitOfWork
{
    private DbContext _context;
    ICustomerRepository Customers { get; private set; }
    IEmployeeRepository Employees { get; private set; } 
    public UnitOfWork(DbContext context, ICustomerRepository cust, IEmployeeRepository emp)
    {
        _context = context;
        Customers = cust;
        Employees = emp;
    }    
}

但是,由于它们都必须共享相同的 DbContext,因此我看不到将它们注入作为一种选择。

这就是为什么我正在考虑创建一个RepositoryFactory类,但由于所有存储库都继承自通用接口,我发现不可能创建单个存储库Create()方法,因为毕竟它们没有真正的共同祖先作为有效的返回类型。

为了让您有更多的了解,这就是代码的样子:

public interface IRepository<TEntity> where TEntity:class
{
    TEntity Get(int id);
    IEnumerable<TEntity> GetAll();
}

public interface ICustomerRepository : IRepository<Customer>
{
    IEnumerable<Customer> GetSeniorCustomers();
}

public class CustomerRepository : ICustomerRepository
{
    private readonly DbContext _context;
    public CustomerRepository(DbContext context) : base(context)
    {
        _context = context;
    }
// ... implementation of ICustomerRepo here
}

现在,这就是目前的情况:

我希望能够做的是:

public UnitOfWork(DbContext context, RepositoryFactory fac)
{
     _context = context;
     Customers = fac.Create(context, RepoType.Customer);
     Employees = fac.Create(context, RepoType.Employee);
}  

我知道它并没有真正给我任何额外的灵活性,但在我看来,它确实让代码不那么笨重。

但是,正如我之前提到的,我想不出 Create() 方法的有效返回类型。

因此,我想出了在 RepositoryFactory 类中创建多个方法的想法,而不是单个参数化方法,如下所示:

public class RepositoryFactory
{
    public ICustomerRepository CreateCustomerRepo(DbContext context){/*...*/}
    public IEmployeeRepository CreateEmployeeRepo(DbContext context){/*...*/} 
}

所以问题是:

  1. 我正在做的事情可以称为工厂方法吗?
  2. 如果不是,这至少是一个有效的解决方案吗?如果没有,我该如何达到同样的目的以更干净的方式?

By 达到同样的目的我的意思是实现一种以易于管理、简洁的方式创建这些存储库的方法。

感谢您提前提供的所有帮助。


首先,明确你的目标。存储库模式存在(至少)有 3 个关键原因:

1)抽象出数据层。就 EF 而言,如果这是您的目标,那么存储库模式就不再有利。尝试从实体框架中抽象应用程序带来的麻烦远大于其价值。您最终将得到一个/两个残缺的 DAL,其中 EF 可以提供的强大功能不可用或效率低下/缓慢,或者是一堆非常复杂的存储库方法,其中包含表达式和其他讨厌的东西作为参数。尝试将应用程序从 EF 中抽象出来(例如,如果您可能想要更改为另一个 ORM)是没有意义的。接受 EF 就像您接受在 .Net 中编写应用程序的事实一样。要将 EF 抽象到可以被替换的程度,您最好不要使用它,因为您不会看到 EF 实际提供的任何好处。

2)使业务逻辑更容易测试。 IMO 这仍然是实体框架存储库的有效论据。是的,EF DbContextcan被嘲笑,但它们仍然是一个混乱的行业。模拟存储库并不比任何其他依赖项更难。

3) 充当域看门人。 DDD 等模式旨在锁定针对域对象和服务中的数据的操作。当使用 EF 帮助包含负责操作域的方法时,存储库模式可以对此提供帮助。对于纯 DDD,我不推荐它们,尽管我也不推荐使用实体作为 DDD 域对象。我确实使用存储库模式来管理 CRUD 的 C.R. 和 D. 方面,并依靠视图模型来封装 U 周围的域逻辑。

我发现在服务点 2 和 3 中最常用的存储库模式是废除通用存储库的非常常见的概念,而是更符合 MVC 中处理控制器的方式来处理存储库。除了视图和模型之间,模型和数据之间。存储库是一个为控制器(在 MVC 中)提供服务的类,它负责创建、读取(在核心级别)和删除实体。这种模式与工作单元结合起来效果很好。 (https://github.com/mehdime/DbContextScope https://github.com/mehdime/DbContextScope是我采用的实现。)

在创建实体时,它负责确保提供所有必需的(不可为空)值和引用,返回与 DbContext 关联的实体,准备就绪。实际上是一个实体工厂。您可能会争论关注点分离,但考虑到存储库已经可以访问 DbContext 来检索相关实体,这几乎是这项工作的最佳场所。

在读取实体中,它通过提供一个IQueryable<TEntity>,执行核心级别规则,例如.Where(x => x.IsActive)例如,用于软删除场景,或基于可以揭示当前用户的依赖关系的身份验证/授权/租赁过滤器。通过暴露IQueryable您可以保持存储库实现简单,并让消费者(控制器)控制数据的使用方式。这可以利用延迟执行来:

  • 仅选择视图模型所需的数据。
  • 执行计数和存在检查。 (.Any())
  • 针对特定用例自定义整个实体结构的过滤逻辑。
  • 执行分页。
  • 根据需要获取尽可能多的(.ToList()、.Take())或尽可能少的(.SingleOrDefault()、.FirstOrDefault())数据。

Read 方法非常容易模拟,并且存储库实现占用的空间非常小。消费者需要意识到他们正在与实体打交道,并处理 EF 及其代理的细微差别,但消费者是工作单元(DbContext 的生命周期)的守护者,因此向其隐藏这一事实是相当没有意义的。将复杂的查询表达式作为参数传递到存储库方法中,让消费者同样有责任了解 EF 的细微差别。调用提供给通用存储库的私有方法以进入Where 子句的表达式也会同样快地破坏事情。沿着这条路线走下去就会遇到上面的第 1 点,不要从你的应用程序中抽象出 EF。

在删除实体时,它确保实体及其关联得到正确管理,无论是硬删除还是软删除。

我避免使用通用存储库,因为就像控制器(和视图)将处理任意数量的相关域视图模型一样,这意味着它们将需要处理许多相关的数据实体。针对任何一个实体的行动总是与针对其他附属实体的行动联系在一起。对于通用存储库,这种分离意味着 a) 越来越多的依赖项和 b) 执行琐碎废话的通用方法和大量自定义代码来处理有意义的内容,或者尝试以通用(基本)方式促进它的复杂代码。通过每个控制器有一个存储库,也许还有一些针对公共实体的真正通用的共享存储库。 (例如查找)我的存储库被明确设计为服务于应用程序的一个区域,并且只有一个理由进行更改。当然,可能有 2 个或更多屏幕需要存储库中的相同行为,但随着应用程序或服务的这些方面的成熟,它们的存储库可以根据需要成熟/优化,而不会产生副作用。 SRP 和 KISS 轻松胜过 DNRY。

一般来说,泛型类有其用途,但几乎在我看到开发人员编写它们的任何情况下,我都会认为这是不成熟的优化。从非泛型实现开始,然后随着产品的成熟,优化泛型into代码而不是尝试围绕它们设计架构。结果几乎总是意识到您需要对它们进行去优化,或者“聪明”地解决发现的模式阻碍开发的限制。

不管怎样,除了“EF 不需要存储库”之外,还有一些值得深思的地方:)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何通用地创建从泛型类型继承的存储库? 的相关文章

随机推荐

  • 为什么我的 MVC 应用程序中有两个 web.config 文件

    Views 文件夹中有一个 应用程序的根目录中还有另一个 我想注册一个自定义处理程序 但我不明白代码应该放在哪里 我在集成模式下运行 IIS7 所以我必须添加
  • C++11 中的线程池

    相关问题 关于 C 11 C 11 std 线程池 https stackoverflow com questions 12993451 c11 stdthread pooled C 11 中的 async launch async 是否会
  • NSScrollView 具有未剪辑的内容视图?

    有没有办法设置我的滚动视图不剪辑其内容 这是一个NSTextView 我有 NSScrollView 的子类并想要它的内容not被剪裁到其边界 我尝试过重写 BOOL wantsDefaultClipping return NO in My
  • 如何让 Flask-SQLAlchemy 在出现异常时自动回滚会话?

    我想设置一个使用以下构建的应用程序Flask SQLAlchemy如果视图引发在视图代码外部冒泡的异常 即未在内部捕获 则回滚对数据库所做的所有更改 我希望它能够工作 即使某些对象在子事务中自动或直接通过session commit 类似的
  • 在keras中构建多输出模型时出错

    我正在尝试在 Keras 中创建一个多输出模型 该模型从卷积开始 旨在叠加两个独立密集层的结果 我为回归任务创建了一些随机数据 其中x1是输入和df是标签 这df包含三列 定义训练和测试拆分并形成模型后 我在拟合模型时收到错误 谁能帮我纠正
  • 如何在 Android 中的搜索输入字段旁边添加下拉菜单?

    在我的 HTC Desire Froyo 上的系统范围搜索中 我看到搜索输入字段左侧有一个小下拉菜单 允许选择我要搜索的位置 全部 网络 应用程序 我怎样才能实现这个在我的一个应用程序中 Google 开发者网站上的搜索教程没有解决这个问题
  • 配置 MapReduce 作业时使用多个 InputFormat 类

    我想编写一个可以处理文本和 zip 文件的 MapReduce 应用程序 为此 我想使用不同的输入格式 一种用于文本 另一种用于 zip 可以这样做吗 从 ChrisWhite的答案延伸一点 你需要的是使用自定义InputFormat an
  • 如何修复 Xcode“DTAssetProviderService 无法启动..”错误?

    我已经升级了 macOS Sierra Developer Preview 但是我的Xcode 7 3 1尝试在模拟器上运行我的项目时出现以下错误 此外 Generic to archieve 还给出了另一个错误 例如 此外 对于存档 发生
  • 如何将此 SQL 查询转换为 LINQ 或 Lambda 表达式?

    我有以下 SQL 查询 SELECT C ID C Name FROM Category C JOIN Layout L ON C ID L CategoryID JOIN Position P ON L PositionID LIKE C
  • 无法使用 Desktop Docker 设置通过节点端口访问 Kubernetes 服务

    我在 Windows 10 上使用 Docker Desktop 我生成 kubernetes NodePort 服务以从客户端 Web 浏览器访问 http 10 110 201 24 30008 hello praveen http 1
  • 停留在基本的 Linq to XML 查询上

    我正在尝试从 namecheap 沙箱 api 中提取信息 但无法弄清楚为什么我的 linq 查询不起作用 这是一个示例响应 XML
  • php 中的文件锁定

    我有一个新人 隔壁的少年 编写了一些 php 代码来跟踪我网站上的一些使用情况 我不熟悉 php 所以我想问一些关于并发文件访问的问题 我的本机应用程序 在 Windows 上 偶尔会通过点击包含我的 php 脚本的 URL 来将一些数据记
  • 从地图外部将对象拖放到 Google 地图中:标记未放置在正确的纬度/经度处

    我想从地图外部将一个对象拖到我的 Google 地图 API V3 中 经过一番研究 我发现这个非常有帮助的帖子 https stackoverflow com a 5921814 1866810我尝试将其适应我的项目 主要思想是在地图上拖
  • MVC路由问题

    我想按如下方式设置路由 Profile Edit gt 编辑操作的路由 Profile Add gt 添加操作的路由 Profile username gt 使用参数 username 路由到 Index 操作 因为操作用户名不存在 所以我
  • EventBus 和 RxJava 有什么区别? [复制]

    这个问题在这里已经有答案了 我对 android 中的 EventBus 和 RxJava 之间的区别感到困惑 我需要实现其中之一来解决我的问题 即在完成某些更改后通知某些组件 以便它们可以更新其状态 另外 我读到 EventsBus 已因
  • Java 禁用 dpi 感知不起作用

    我正在尝试运行 Java 应用程序 Dsun java2d dpiaware false争论但什么也没发生 我希望有一个模糊的用户界面 但对于正常大小的图标和字体 这个标志似乎不起作用 我在 Windows 8 1 上使用 JDK 1 8
  • 从 pySpark SQL 写入远程 mysql 数据库 (JDBC) 获取新行 id

    我正在使用 pyspark sql 使用 JDBC 在远程 mysql 数据库中创建行 我有两张桌子 parent table id value and child table id value parent id 所以每一行parent
  • 如何关闭颜色并删除 Git 日志中的时间?

    正如标题所说 如何关闭颜色并去除时间 因为我需要将输出复制到pbcopy 但颜色模式会使输出包含特殊字符 我已将以下内容添加到 gitconfig文件 以删除大部分颜色 color ui false branch false diff fa
  • 为什么 ConcurrentSkipListSet 升序迭代器比降序迭代器“更快”?

    我在 ConcurrentSkipListSet 上使用 DescendingIterator 方法 我刚刚检查了文档并注意到以下评论 升序视图及其迭代器比降序视图及其迭代器更快 See https docs oracle com java
  • 如何通用地创建从泛型类型继承的存储库?

    我目前正在尝试实施存储库模式在我的 DbContext 之上 问题是 我最终遇到了一种情况 我必须将多个存储库注入到一个UnitOfWork构造函数 像这样 public class UnitOfWork private DbContext