修改现有的通用存储库模式以添加选择

2023-12-10

我想修改现有的通用存储库以添加可选select类似于 Entity Framework Core 中的功能。

期望的结果:

private readonly IUnitOfWork _unit;
// ...

// without using select functionality
IEnumerable<Entity> entities = await _unit.Account.AllAsync();
Entity? x = await _unit.Account.SingleAsync(x => x == id);

// using select functionality
IEnumerable<DTO> y = await _unit.Account.AllAsync(select: x => new DTO
{
    Name = x.Name
});
DTO? y = await _unit.Account.SingleAsync(x => x == id, select: x => new DTO
{
    Name = x.Name
});

我尝试从这个问题实施解决方案在通用存储库函数中选择特定列但参数是必需的,我希望它是可选的。

为简单起见,我只在通用存储库中保留要添加此功能的方法:

in IBaseRepository.cs

public interface IBaseRepository<T> where T : BaseEntity
{
    Task<IEnumerable<T>> AllAsync(
        Expression<Func<T, bool>>? filter = null,
        Func<IQueryable<T>, IOrderedQueryable<T>>? order = null, 
        Func<IQueryable<T>, IIncludableQueryable<T, object>>? include = null,
        int skip = 0,
        int take = int.MaxValue,
        Track track = Track.NoTracking);

    Task<T?> SingleAsync(
        Expression<Func<T, bool>> filter, Func<IQueryable<T>, 
        IIncludableQueryable<T, object>>? include = null,
        Track track = Track.Tracking);
}

in BaseRepository.cs

public class BaseRepository<T> : IBaseRepository<T> where T : BaseEntity
{
    private readonly DataContext _context;
    internal DbSet<T> _set;

    public BaseRepository(DataContext context)
    {
        _context = context;
        _set = context.Set<T>();
    }

    public async Task<IEnumerable<T>> AllAsync(
        Expression<Func<T, bool>>? filter = null, 
        Func<IQueryable<T>, IOrderedQueryable<T>>? order = null, 
        Func<IQueryable<T>, IIncludableQueryable<T, object>>? include = null,
        int skip = 0, int take = int.MaxValue, Track track = Track.NoTracking)
    {
        IQueryable<T> query = _set;
        switch (track)
        {
            case Track.NoTracking:
                query = query.AsNoTracking();
                break;
            case Track.NoTrackingWithIdentityResolution:
                query = query.AsNoTrackingWithIdentityResolution();
                break;
            default:
                query = query.AsTracking();
                break;
        }
        query = skip == 0 ? query.Take(take) : query.Skip(skip).Take(take);
        query = filter is null ? query : query.Where(filter);
        query = order is null ? query : order(query);
        query = include is null ? query : include(query);
        return await query.ToListAsync();
    }

    public async Task<T?> SingleAsync(
        Expression<Func<T, bool>> filter,
        Func<IQueryable<T>, IIncludableQueryable<T, object>>? include = null,
        Track track = Track.Tracking)
    {

        IQueryable<T> query = _set;
        switch (track)
        {
            case Track.NoTracking:
                query = query.AsNoTracking();
                break;
            case Track.NoTrackingWithIdentityResolution:
                query = query.AsNoTrackingWithIdentityResolution();
                break;
            default:
                query = query.AsTracking();
                break;
        }
        query = filter is null ? query : query.Where(filter);
        query = include is null ? query : include(query);
        return await query.SingleOrDefaultAsync();
    }
}

in BaseEntity.cs(每个类dbtable都会继承自BaseEntity)

public abstract class BaseEntity
{
    public Guid Id { get; set; } = Guid.NewGuid();
    public DateTime CreatedAt { get; set; } = DateTime.Now;
    public DateTime? UpdatedAt { get; set; }
}

诚实地问自己:“为什么我认为我需要通用存储库?”

答:“因为我想在 EF 上添加一个抽象层,这样我就可以在需要时替换它。”

  • 你是自我实现预言的受害者;限制您自己和您的应用程序充分利用 EF 的能力。该解决方案要么非常有限,要么非常复杂且存在问题,以至于 EF 将被认为太慢或无法完成所需的操作,并且需要用其他人说服您更好/更快的解决方案来替换。

答:“因为我不想让领域知识或 EF 特定知识污染我的业务逻辑。”

  • 这是个谎言。将表达式传递到存储库会因领域和 EF 特定的知识和限制而污染领域。如果表达式传递给 EF 的 Linq 方法,它们必须符合域并且必须适合 EF。您删除了对 EF 的引用,但仍然对您的消费者施加 EF 的限制。您不能引用表达式中的方法,也不能使用未映射的属性。 EF 和您选择的提供程序需要能够将这些表达式转换为 SQL,否则您需要编写更多代码来引入您自己的表达式解析器和适配器。

答:“因为我希望能够引入一个抽象点,这样我就可以更轻松地编写单元测试。”

  • 这是一个很好的理由,但可以用一种更简单的方法来解决:

.

IQueryable<T> All();
IQueryable<T> Single(int id);

就是这样。您的消费者可以支持过滤、排序、分页、存在检查、计数、投影,甚至可以自行确定是否存在开销async操作是必要的。模拟起来非常简单,只需增加一点复杂性即可支持传回数据async消耗。存储库方法可以施加基本级别的过滤,例如IsActive对于软删除系统,或检查当前用户的授权并将其纳入返回的结果中。

当您想要引入一个解决方案来满足“DNRY”(不要重复自己)或在您的应用程序中强制执行一定程度的一致性时,或者在完全相同的(不仅仅是类似)关注点,例如 Web API 服务和 Web 应用程序, 这可以通过移交给公共服务来完成,该服务使用存储库以一致的方式获取和打包数据以返回 DTO。否则,像对待 MVC 控制器一样对待存储库,将其范围限定为对一个使用者负责,并且只有一个理由进行更改。

反对返回的典型论点IQueryable缺点是它是一个薄弱或有漏洞的抽象,并为消费者提供了一把上膛的猎枪。这是 100% 正确的,它是一个弱抽象,这使得它很容易被模拟。如果您不需要模拟它进行单元测试,那么您只是“不需要它”。就为开发人员提供武器而言,恕我直言,对于一个项目来说,为实施开发人员提供他们所需的工具,使他们能够针对领域编写有效的表达式以及如何正确执行和纠正的培训/知识要好得多。如果发现错误;而不是尝试将技术抽象出来以“简化”,或者从本质上讲,最终会引入同样多的复杂性和制造混乱的能力,只是在界面上使用您的名字而不是 EF。

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

修改现有的通用存储库模式以添加选择 的相关文章

  • 我如何才能等待多个事情

    我正在使用 C 11 和 stl 线程编写一个线程安全队列 WaitAndPop 方法当前如下所示 我希望能够将一些内容传递给 WaitAndPop 来指示调用线程是否已被要求停止 如果 WaitAndPop 等待并返回队列的元素 则应返回
  • 以文化中立的方式将字符串拆分为单词

    我提出了下面的方法 旨在将可变长度的文本拆分为单词数组 以进行进一步的全文索引处理 删除停止词 然后进行词干分析 结果似乎不错 但我想听听关于这种实现对于不同语言的文本的可靠性的意见 您会建议使用正则表达式来代替吗 请注意 我选择不使用 S
  • 按成员序列化

    我已经实现了template
  • ASP.NET MVC:这个业务逻辑应该放在哪里?

    我正在开发我的第一个真正的 MVC 应用程序 并尝试遵循一般的 OOP 最佳实践 我正在将控制器中的一些简单业务逻辑重构到我的域模型中 我最近一直在阅读一些内容 很明显我应该将逻辑放在域模型实体类中的某个位置 以避免出现 贫血域模型 反模式
  • Asp.NET WebApi 中类似文件名称的路由

    是否可以在 ASP NET Web API 路由配置中添加一条路由 以允许处理看起来有点像文件名的 URL 我尝试添加以下条目WebApiConfig Register 但这不起作用 使用 URIapi foo 0de7ebfa 3a55
  • 将 VSIX 功能添加到 C# 类库

    我有一个现有的单文件生成器 位于 C 类库中 如何将 VSIX 项目级功能添加到此项目 最终目标是编译我的类库项目并获得 VSIX 我实际上是在回答我自己的问题 这与Visual Studio 2017 中的单文件生成器更改 https s
  • 使用 WebClient 时出现 System.Net.WebException:无法创建 SSL/TLS 安全通道

    当我执行以下代码时 System Net ServicePointManager ServerCertificateValidationCallback sender certificate chain errors gt return t
  • 控件的命名约定[重复]

    这个问题在这里已经有答案了 Microsoft 在其网站上提供了命名指南 here http msdn microsoft com en us library xzf533w0 VS 71 aspx 我还有 框架设计指南 一书 我找不到有关
  • 如何序列化/反序列化自定义数据集

    我有一个 winforms 应用程序 它使用强类型的自定义数据集来保存数据进行处理 它由数据库中的数据填充 我有一个用户控件 它接受任何自定义数据集并在数据网格中显示内容 这用于测试和调试 为了使控件可重用 我将自定义数据集视为普通的 Sy
  • 如何查看网络连接状态是否发生变化?

    我正在编写一个应用程序 用于检查计算机是否连接到某个特定网络 并为我们的用户带来一些魔力 该应用程序将在后台运行并执行检查是否用户请求 托盘中的菜单 我还希望应用程序能够自动检查用户是否从有线更改为无线 或者断开连接并连接到新网络 并执行魔
  • 覆盖子类中的字段或属性

    我有一个抽象基类 我想声明一个字段或属性 该字段或属性在从该父类继承的每个类中具有不同的值 我想在基类中定义它 以便我可以在基类方法中引用它 例如覆盖 ToString 来表示 此对象的类型为 property field 我有三种方法可以
  • 链接器错误:已定义

    我尝试在 Microsoft Visual Studio 2012 中编译我的 Visual C 项目 使用 MFC 但出现以下错误 error LNK2005 void cdecl operator new unsigned int 2
  • WPF/C# 将自定义对象列表数据绑定到列表框?

    我在将自定义对象列表的数据绑定到ListBox in WPF 这是自定义对象 public class FileItem public string Name get set public string Path get set 这是列表
  • cmake 将标头包含到每个源文件中

    其实我有一个简单的问题 但找不到答案 也许你可以给我指一个副本 所以 问题是 是否可以告诉 cmake 指示编译器在每个源文件的开头自动包含一些头文件 这样就不需要放置 include foo h 了 谢谢 CMake 没有针对此特定用例的
  • 是否可以在 .NET Core 中将 gRPC 与 HTTP/1.1 结合使用?

    我有两个网络服务 gRPC 客户端和 gRPC 服务器 服务器是用 NET Core编写的 然而 客户端是托管在 IIS 8 5 上的 NET Framework 4 7 2 Web 应用程序 所以它只支持HTTP 1 1 https le
  • IEnumreable 动态和 lambda

    我想在 a 上使用 lambda 表达式IEnumerable
  • 哪种 C 数据类型可以表示 40 位二进制数?

    我需要表示一个40位的二进制数 应该使用哪种 C 数据类型来处理这个问题 如果您使用的是 C99 或 C11 兼容编译器 则使用int least64 t以获得最大的兼容性 或者 如果您想要无符号类型 uint least64 t 这些都定
  • Windows 和 Linux 上的线程

    我在互联网上看到过在 Windows 上使用 C 制作多线程应用程序的教程 以及在 Linux 上执行相同操作的其他教程 但不能同时用于两者 是否存在即使在 Linux 或 Windows 上编译也能工作的函数 您需要使用一个包含两者的实现
  • 如何在文本框中插入图像

    有没有办法在文本框中插入图像 我正在开发一个聊天应用程序 我想用图标图像更改值 等 但我找不到如何在文本框中插入图像 Thanks 如果您使用 RichTextBox 进行聊天 请查看Paste http msdn microsoft co
  • 对来自流读取器的过滤数据执行小计

    编辑问题未得到解答 我有一个基于 1 个标准的过滤输出 前 3 个数字是 110 210 或 310 给出 3 个不同的组 从流阅读器控制台 问题已编辑 因为第一个答案是我给出的具体示例的字面解决方案 我使用的实际字符串长度为 450 个

随机推荐

  • 使用多个“for”循环解码嵌套 JSON

    我是 Python 新手 上周 并且已经达到了我的极限 在这个问题上花了三天时间 大部分时间都在 stackoverflow 但我不知道如何继续下去 The Json有多个嵌套数组 它可以包含三个 如下面的示例 json txt 所示 或
  • 使用 pyinstaller 创建 Django exe 时静态文件的位置

    我有一个具有以下结构的 Django 项目 根 视频 静止的 模板 settings py 中的 STATIC URL 设置为 STATIC URL static 我设法使用 pyinstaller 从 manage py 创建一个 Win
  • HTML5 验证错误:在此上下文中不允许元素源作为音频的子元素

    这是我作业中的一段代码 我检查了 w3 样本http www w3schools com html html5 audio asp并且格式正确 我是因为这些错误而发疯还是我错过了什么 我相信这就是 HTML5source元素不喜欢类型的编写
  • tsconfig.json 中的 target 是做什么用的?

    什么是target in tsconfig json表示 compilerOptions sourceMap true target es5 module commonjs jsx react moduleResolution classi
  • Google 应用程序脚本中的 Adword 脚本

    我无法在 Google App 脚本中找到 Adword API 支持 我可以从 adword 帐户编写 adword 脚本 但目前我正在处理多个帐户 几乎 25 个 我要做的就是进入个人帐户并在其中复制我的脚本帐户和adword脚本有一个
  • 在pykml中读取带有多个地标的kml文件

    In pykml 我可以使用以下代码读取文件中的第一个地标 with open filename as f pm parser parse f getroot Document Folder print got print pm Place
  • 使用 Storyboard 和 Xcode 4.5 的 UIPageViewController

    我想问一些关于 Xcode 4 5 的问题 我正在使用故事板创建应用程序 这个应用程序有UIPageViewController 但是如果我想定义标识符UIViewController在情节提要的 IB 中 已经没有地方了 你能告诉我 我可
  • 如何进行跨站ajax请求?

    浏览器不允许跨站点 AJAX 调用 这是安全限制 有什么可能的解决办法吗 EDIT 我只控制来电者网站 如果你控制双方 那么就有很多选择 例如JSONP or 修改标题远程网站的响应 很遗憾 JSONP 仅在远程网站支持时才有效 你can
  • Opencv:在盒子中找到盒子

    后勤 opencv 2 4 和 Python 2 7 The image I m working with 问题 我感兴趣的是隔离围绕 9 条垂直线和水平线形成盒子的轮廓 我只是不确定该怎么做 我看过各种教程 例如关于数独谜题的教程 这些教
  • 通过引用临时传递函数的生命周期[重复]

    这个问题在这里已经有答案了 我可以做这样的事情 const int i 5 并将临时的生命周期延长到i 但怎么样 const int fun const int i return i int main const int r fun 5 C
  • matplotlib - 更改图大小但保持字体大小不变

    我想显示几个不同大小的图形 确保打印图形时文本始终具有相同的大小 我怎样才能做到这一点 举个例子 假设我有两个数字 import matplotlib pylab as plt import matplotlib as mpl mpl rc
  • Angular 9 通用错误:组件“HeaderComponent”未解析:

    更新到 Angular 9 和 Universal 9 后 运行时出现错误npm run build ssr npm run serve ssr Error Component HeaderComponent is not resolved
  • 疯狂邮件方法不起作用?

    在配置菜单下的 spree 管理面板中 我配置了邮件 smtp 端口等 创建新邮件方法后 我按下测试邮件按钮 我收到以下警报消息 测试邮件发送成功 甚至终端窗口也显示电子邮件发送成功消息 但是邮件未送达相应地址 发送测试邮件后控制台日志 S
  • 将装置转换为 Rails 中的 Factory Girl

    我想将我的装置迁移到 Rails 中的 Factory Girl 有没有简单的方法可以转换factories rb 文件中的所有yml 文件 我假设您想要做的是找到一个脚本来查看您的模型并为它们生成工厂 我尝试过一次 使用除了factory
  • 如何选择最大连接池大小?

  • 如何向彩色图像添加噪声 - Opencv

    我正在尝试向图像添加噪声 然后对其进行降噪以测试我的降噪算法 所以对于基准我指的是这个在线测试样品 我正在尝试复制噪声模型 参考这个线程1 2我正在像这样向图像添加噪点 Mat mSource Bgr mSource Bgr imread
  • 如何在二叉搜索树中迭代添加元素?

    public void Insert int value if value lt Data if LeftNode null LeftNode new TreeNode value else LeftNode Insert value el
  • build.gradle 中的 gradle 任务方法语法

    我是 gradle 和 groovy 的新手 我正在阅读 Gradle 的 usr 指南 并对任务方法有一些语法问题 task intro dependsOn hello doLast println I m Gradle 问题1 上面的代
  • Excel / VBA 通过交叉引用 2 个不同的工作表然后删除 1 行来删除重复行

    我有 2 个单独的工作表 我们称它们为工作表 A 工作表 B 我在工作表 B 中有数据 该数据也在工作表 A 中 我想找到那些相等的行并将它们从工作表 B 中删除 我无法合并两张表并使用过滤器 因为我正在执行动态 SQL 来查询不同的数据
  • 修改现有的通用存储库模式以添加选择

    我想修改现有的通用存储库以添加可选select类似于 Entity Framework Core 中的功能 期望的结果 private readonly IUnitOfWork unit without using select funct