首页 > 其他分享 >DbContext&Repository基本实现

DbContext&Repository基本实现

时间:2023-10-29 19:02:52浏览次数:43  
标签:基本 Task Repository await dbContext order DbContext id public

前言

对于仓储模式,各有看法不同,直接使用DbContext简单方便,使用仓储模式扩展复用较好。受限于场景的差异,人员技能熟悉程度,交付时间,成本等选择哪种方式也有不同。


Controller&DbContext

当需要快速设计一个访问数据库Demo时,顺手便是Controller+DbContext,当然还有其他更为简便的方式,但是这套模式在使用频次上更高。

图片

DbContext内部提供了常用的一些方法,直接操作DbContext很是方便,很自由,想怎么取怎么取,同时因为DbContext内部封装好了事务,因此多次变更一次提交之类的都很方便。

[ApiController]
[Route("[controller]")]
public class OrderController : ControllerBase
{
    private readonly AppDbContext _dbContext;

    public OrderController(AppDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    [HttpGet("{id}")]
    public async Task<Order> GetAsync(int id)
    {
        return await _dbContext.Orders.AsNoTracking().FirstAsync(o => o.Id == id);
    }

    [HttpGet]
    public async Task<List<Order>> GetListAsync()
    {
        return await _dbContext.Orders.AsNoTracking().ToListAsync();
    }

    [HttpPost]
    public async Task<int> CreateAsync(string name)
    {
        var order = new Order()
        {
            Name = name,
            CreateDate = DateTime.UtcNow,
        };
        await _dbContext.Orders.AddAsync(order);
        await _dbContext.SaveChangesAsync();

        return order.Id;
    }

    [HttpPut("{id}")]
    public async Task<int> UpdateAsync(int id, string name)
    {
        var order = await _dbContext.Orders.FirstAsync(o=>o.Id == id);
        order.Name = name;
        await _dbContext.SaveChangesAsync();
        return order.Id;
    }

    [HttpDelete("{id}")]
    public async Task DeleteAsync(int id)
    {
        var order = await _dbContext.Orders.FirstAsync(o => o.Id == id);
        _dbContext.Orders.Remove(order);
        await _dbContext.SaveChangesAsync();
    }
}

可是使用方便的同时,有其他问题也需要考虑,如上代码如需要在其他Controller中查询,是不是又得写一遍,各种操作都得再来一遍,代码重复度很高。查询部分需要更加优化的查询性能等,受限EFCore的查询,需要更换到Dapper,那么纯查询操作的接口需要大面积改代码,相比下,引入仓储模式可能更加容易扩展,代码重用上也更佳。
图片

对于仓储模式结合DbContext,可以见到很多观点,有人认为多此一举,DbContext本身已经具备仓储功能及工作单元,没必要再在DbContext基础上再去封装。也有人认为需要封装,能够减少耦合,可扩展性更强。微软官方文档里也对此给了自己的看法,建议在复杂场景中使用仓储模式,以能更低成本的测试代码。


仓储模式

仓储模式(Repository Pattern)是一种设计模式,它在领域层和数据访问层(如实体框架核心/Dapper)之间协调数据。仓储隐藏了存储或检索数据所需的逻辑。因此,我们的应用程序不会关心我们使用什么类型的 ORM,因为使用EFCore、Dapper或是直接使用Ado.Net都是在仓储内部进行,甚至于说,在仓储中读写文件来记录数据,而对外而言,还是不变的操作,只是底层的存储介质,仓储内部的操作逻辑变更为文件形式。如此一来,在业务逻辑与底层实现之间,能够更清楚地分离关注点。

图片


实现基本仓储模式

在Controller&DbContext的基础上,封装一层Repository,实现基本的仓储模式。解决方案与项目搭建略过。

图片

  • Api层承载项目运行。
  • Domain层管理实体与仓储接口。
  • Infrastructure层管理DbContext与仓储实现。

仓储接口定义

按照Controller&DbContext中使用到的操作,实现对Order的仓储接口定义,暂忽略泛型仓储的考虑。

public interface IOrderRepository
{
    Task<Order> GetAsync(int id);
    Task<List<Order>> GetListAsync();
    Task<Order> InsertAsync(Order order);
    Task<Order> UpdateAsync(Order order);
    Task DeleteAsync(Order order);
}

仓储接口的实现

实现过程则是对DbContext的操作,仅将Controller&DbContext中操作DbContext部分分离到Repository中。

public class OrderRepository : IOrderRepository
{
    private readonly AppDbContext _dbContext;

    public OrderRepository(AppDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public async Task<Order> GetAsync(int id)
    {
        return await _dbContext.Orders.AsNoTracking().FirstAsync(o => o.Id == id);
    }

    public async Task<List<Order>> GetListAsync()
    {
        return await _dbContext.Orders.AsNoTracking().ToListAsync();
    }

    public async Task<Order> InsertAsync(Order order)
    {
        await _dbContext.Orders.AddAsync(order);
        await _dbContext.SaveChangesAsync();
        return order;
    }

    public async Task<Order> UpdateAsync(Order order)
    {
        _dbContext.Update(order);
        await _dbContext.SaveChangesAsync();
        return order;
    }

    public async Task DeleteAsync(Order order)
    {
        _dbContext.Orders.Remove(order);
        await _dbContext.SaveChangesAsync();
    }
}


Controller中使用仓储接口

如此一来,Controller中变更为对仓储门面的操作了。

[ApiController]
[Route("[controller]")]
public class OrderController : ControllerBase
{
    private readonly IOrderRepository _orderRepository;

    public OrderController(IOrderRepository orderRepository)
    {
        _orderRepository = orderRepository;
    }

    [HttpGet("{id}")]
    public async Task<Order> GetAsync(int id)
    {
        return await _orderRepository.GetAsync(id);
    }

    [HttpGet]
    public async Task<List<Order>> GetListAsync()
    {
        return await _orderRepository.GetListAsync();
    }

    [HttpPost]
    public async Task<int> CreateAsync(string name)
    {
        var order = new Order()
        {
            Name = name,
            CreateDate = DateTime.UtcNow,
        };
        order = await _orderRepository.InsertAsync(order);
        return order.Id;
    }

    [HttpPut("{id}")]
    public async Task<int> UpdateAsync(int id, string name)
    {
        var order = await _orderRepository.GetAsync(id);
        order.Name = name;
        order = await _orderRepository.UpdateAsync(order);
        return order.Id;
    }

    [HttpDelete("{id}")]
    public async Task DeleteAsync(int id)
    {
        var order = await _orderRepository.GetAsync(id);
        await _orderRepository.DeleteAsync(order);
    }
}

接口测试&Mock仓储

DBContext已经以非常少的努力代表了存储库和UoW(工作单元)实现。并且可以在代码的任何地方使用DBContext(从管道或中间件中依赖注入),也可以直接使用。但是,如果想要能够执行测试,却是相对繁琐,使用上仓储模式后,再以单元测试模式来测试下接口。

public class OrderControllerUnitTest
{
    [Test]
    public async Task GetAllOrders_ShouldReturnAllOrders()
    {
        // Arrange
        var mockRepo = new Mock<IOrderRepository>();
        mockRepo.Setup(repo => repo.GetListAsync())
            .ReturnsAsync(GetTestOrders());
        var controller = new OrderController(mockRepo.Object);

        // Act
        var result = await controller.GetListAsync();

        // Assert
        Assert.That(result.Count, Is.EqualTo(4));
    }

    [Test]
    public async Task GetOrderAsync_ShouldReturnOrder()
    {
        // Arrange
        var mockRepo = new Mock<IOrderRepository>();
        mockRepo.Setup(repo => repo.GetAsync(It.IsAny<int>()))
            .ReturnsAsync(GetTestOrder());
        var controller = new OrderController(mockRepo.Object);

        // Act
        var result = await controller.GetAsync(1);

        // Assert
        Assert.That(result.Id, Is.EqualTo(1));
    }

    [Test]
    public void GetOrderAsync_ShouldThrowException()
    {
        // Arrange
        var mockRepo = new Mock<IOrderRepository>();
        mockRepo.Setup(repo => repo.GetAsync(It.IsAny<int>()))
            .Throws(new Exception("The entity does not exist."));
        var controller = new OrderController(mockRepo.Object);

        // Assert,Act
        Assert.ThrowsAsync<Exception>(async () =>
        {
            await controller.GetAsync(0);
        });
    }

    #region MockData
    private List<Order> GetTestOrders()
    {
        var testOrders = new List<Order>
        {
            new Order { Id = 1, Name = "Demo1", CreateDate = DateTime.Now },
            new Order { Id = 2, Name = "Demo2", CreateDate = DateTime.Now },
            new Order { Id = 3, Name = "Demo3", CreateDate = DateTime.Now },
            new Order { Id = 4, Name = "Demo4", CreateDate = DateTime.Now }
        };
        return testOrders;
    }

    private Order GetTestOrder()
    {
        var testOrder = new Order { Id = 1, Name = "Demo1", CreateDate = DateTime.Now };
        return testOrder;
    }
    #endregion
}

参考资料

https://codewithmukesh.com/blog/repository-pattern-in-aspnet-core/

https://www.thereformedprogrammer.net/is-the-repository-pattern-useful-with-entity-framework-core/

https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application

https://procodeguide.com/programming/repository-pattern-in-aspnet-core/

https://learn.microsoft.com/en-us/ef/core/testing/choosing-a-testing-strategy#repository-pattern

https://learn.microsoft.com/en-us/ef/core/testing/testing-without-the-database#repository-pattern


2023-10-29,望技术有成后能回来看见自己的脚步。

标签:基本,Task,Repository,await,dbContext,order,DbContext,id,public
From: https://www.cnblogs.com/CKExp/p/17796196.html

相关文章

  • C++多线程编程——线程的基本概念和使用方法
    什么是线程?在计算机科学中,线程是进程中的一个执行控制单元,也被称为执行路径。每个进程可以包含多个线程,每条线程并行执行不同的任务。线程是操作系统可识别的最小执行和调度单位。进程和线程的区别进程是程序在某个数据集合上的一次运行活动,也是操作系统进行资源分配和保护的......
  • coredns基本原理
    k8s1.19.0coredns1.7.0dnsip获取过程dns请求到达coredns后,从list/watch本地缓存indexer获取对象,返回结果。外部域名返回用户配置的IP。有状态应用Pod域名:pod-name.svc-name.namespace.svc.cluster.local返回PodIP。HeadlessCluster:svc-name.namespace.svc.cluster.local......
  • 基本概念与notepad
    1基本概念JDKJava开发者工具,包含jre,jvmJREjava运行时环境JVMjava虚拟机javad的跨平台主要是用了虚拟机  2java开发环境搭建JDK下载与安装还是用原来的17没有用8配置环境变量JDK目录介绍 helloworld及简单语法规则先建一个文件夹code,再里面建一个......
  • linux系统 基本权限ACL读书笔记
    当你作为一个Linux初学者探索文件权限和ACL(AccessControlLists)时,了解getfacl和setfacl命令将帮助你更好地管理文件和目录的权限。以下是一些关于这两个命令的读书笔记:getfacl命令getfacl命令用于获取文件或目录的ACL信息。ACL允许你在标准UNIX权限之外更精细地控制访问。以下......
  • 基本示波器和波形发生器测量实验
    本示波器实验指南和教程适用于随教育培训套件(DSOXEDK)一同许可的 KeysightInfiniiVision2000,3000X系列示波器和4000X系列示波器。基本示波器和波形发生器测量实验示波器基本实验#1:对正弦波执行测量示波器基本实验#2:了解示波器触发的基本知识示波器基本实验#......
  • 数据结构与算法-基本概念
    什么是数据结构与算法从广义上讲数据结构就是指一组数据的存储结构。算法就是操作数据的一组方法。从狭义上讲,是指某些著名的数据结构和算法,比如队列、栈、堆、二分查找、动态规划等。这些都是前人智慧的结晶,我们可以直接拿来用......
  • DSPLearning_dayONE___________matlab实现DTFT里面的一些常用函数以及基本运算
    DSPmatlab实现\(\delta(n)\)的实现%matlab中坐标轴的横坐标和纵坐标是分开表示的n=-10:20;%横坐标的显示范围这个是确定了x轴的坐标范围delta=[zeros(1,10)1zeros(1,20)];%zeros(m,n)产生一个mxn的全零矩阵这个是每个x轴对应的y轴的值stem(n,delta);gridon......
  • EF Core 基本使用
    一、与数据库表映射1、安装依赖:Install-PackageMicrosoft.EntityFrameworkCore.SqlServer;2、创建实体类:publicrecordPerson{publiclong?Id{get;set;}publicstringName{get;set;}publicstringBloodType{get;set;}......
  • Java基本语法_04类型转换
    1.自动类型转换 ......
  • TestNG的基本传参使用
            万事开头难,其实开过头后也不容易。测试方面的技术好多啊,多的像天上的繁星,一.俩.仨.四个…在有限的时间里,想做最多的了解,只能从最实用的着手,去除教条式的金科玉律。于是,一个切入点蹦了出来,那就是介绍号称下一代Java测试技术的TestNG。其实这个nextgeneration也只......