在 .Net 5 中使用依赖注入实现工作单元和存储库模式

作者 : 慕源网 本文共7592个字,预计阅读时间需要19分钟 发布时间: 2021-09-25 共326人阅读

介绍

所有软件程序都需要一个统计基础框架,它在其中进行通信 (CRUD) 以保存和获取更好的数据。市场上许多可用的技术和框架都可以顺利有效地访问数据库。对于使用 Microsoft .Net 技术的构建者,Entity Framework 或 Dapper 已获得认可。但是,问题并不总是使用这些框架的方式,而是使用可重用、可维护和可读的框架编写代码的方式。本文将暂时澄清工作配置实例的存档和单元,以处理记录集准入和企业原因交换的所有常规问题。我们同样包含了一些在常规 .Net Core 项目中执行它们的方法的功能实例

目录

  • 什么是存储库和工作单元模式
  • 先决条件
  • 设置项目
  • 实现存储库模式
  • 通用存储库接口
  • 存储库类
  • 存储库和 DbContext 注入
  • 依赖注入
  • 工作界面设计单元
  • 工作实施单位
  • 模式执行
  • 包装 API 控制器
  • 项目结构
  • 结论

什么是存储库和工作单元模式?

存储库模式旨在在应用程序的数据访问层和业务逻辑层之间创建一个抽象层。它是一种数据访问模式,促使采用更松散耦合的数据访问方法。我们在一个单独的类或一组称为存储库的类中创建数据访问逻辑,负责持久化应用程序的业务模型。

工作单元被称为单个事务,涉及插入/更新/删除等多种操作。简单来说,就是对于特定的用户操作(比如在网站上注册),所有的事务,比如插入/更新/删除等,都是在一个事务中完成的,而不是做多个数据库事务。这意味着,这里的一个工作单元涉及插入/更新/删除操作,所有这些都在一个事务中。

在 .Net 5 中使用依赖注入实现工作单元和存储库模式

先决条件

  • Visual Studio 2019 – 从这里下载 
  • .Net 5.0 SDK – 从这里下载 

设置项目

  1. 打开 Visual Studio 并选择“创建新项目”,然后单击“下一步”按钮。
  2. 添加“项目名称”和“解决方案名称”并选择在该位置保存项目的路径,单击“下一步”。
  3. 现在选择我们安装 SDK 后获得的目标框架“.Net 5.0”,并且还将获得另一个选项来默认使用该复选框选项配置 Open API 支持。

实现存储库模式

现在让我们跳入代码以设置存储库模式来持久化域模型。这里我们需要添加一个新的类库项目 OrderStore.Domain 来添加我们的领域模型和存储库层,如下所示,领域项目代表具有必要业务逻辑的领域层,存储库层代表持久性方面。为了简单起见,域层包含两个聚合(Order 和 Product),每个聚合都有一个实体。解决方案设置如下

在 .Net 5 中使用依赖注入实现工作单元和存储库模式

Order.cs

using System;
using System.ComponentModel.DataAnnotations;

namespace OrderStore.Domain.Models
{
    public class Order
    {
        [Key]
        public int OrderId { get; set; }
        public string OrderDetails { get; set; }
        public bool IsActive { get; set; }
        public DateTime OrderedDate { get; set; }
    }
}

Product.cs

using System.ComponentModel.DataAnnotations;

namespace OrderStore.Domain.Models
{
   public class Product
    {
        [Key]
        public int ProductId { get; set; }
        public string Name { get; set; }
        public float Price{ get; set; }
        public bool isDisCountApplied { get; set; }
    }
}

通用存储库接口

Generic Repository 帮助我们从数据库中保存和检索其持久状态。

IGenericRepository.cs

using System.Collections.Generic;
using System.Threading.Tasks;

namespace OrderStore.Domain.Interfaces
{
    public interface IGenericRepository<T> where T : class
    {
        Task<T> Get(int id);
        Task<IEnumerable<T>> GetAll();
        Task Add(T entity);
        void Delete(T entity);
        void Update(T entity);
    }
}

现在这两个实体代表 Order 和 Product 域对象。这两个对象有自己的集合,因为我们为每个实体类型添加了通用存储库接口来管理所有 Crud 操作。我们可以重用通用存储库,如下所示。

IOrderRepository.cs

using OrderStore.Domain.Models;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace OrderStore.Domain.Interfaces
{
    public interface IOrderRepository : IGenericRepository<Order>
    {
       Task<IEnumerable<Order>> GetOrdersByOrderName(string orderName);
    }
}

IProductRepository.cs

using OrderStore.Domain.Models;

namespace OrderStore.Domain.Interfaces
{
    public interface IProductRepository: IGenericRepository<Product>
    {
    }
}

存储库类

存储库层实现在每个根中定义的接口。这允许将存储库实现从域层中抽象出来。Repository 实例使用 EFCore 连接到 SQL Server 实例并执行数据库操作。要使用 EFCore,我们需要将以下Nuget 包安装 到 OrderStore.Repository  (创建一个新项目(类库))。

在 .Net 5 中使用依赖注入实现工作单元和存储库模式

在我们实现存储库类之前,我们需要实现 DbContext 类以连接到数据库。存储库和数据库之间的 DbContext 实现。

应用程序数据库上下文.cs

using Microsoft.EntityFrameworkCore;
using OrderStore.Domain.Models;

namespace OrderStore.Repository
{
   public class ApplicationDbContext : DbContext
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
        { }

        public DbSet<Order> Orders { get; set; }
        public DbSet<Product> Products { get; set; }

    }

}

现在我们有了连接数据库的 DbContext 类,我们可以创建实体 Order 和 Product 使用的 Repository 的必要实现。分类的存储库现在可以从通用抽象存储库类继承并实现特定于该实体的功能。这是我们现在可以使用命名查询返回特定业务数据的存储库模式的最大优势之一

OrderRepository.cs

using OrderStore.Domain.Interfaces;
using OrderStore.Domain.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;

namespace OrderStore.Repository
{
   public class OrderRepository: GenericRepository<Order>, IOrderRepository
    {
        public OrderRepository(ApplicationDbContext context): base(context)
        {

        }
        public async Task<IEnumerable<Order>> GetOrdersByOrderName(string orderName)
        {
            return await _context.Orders.Where(c=>c.OrderDetails.Contains(orderName)).ToListAsync();
        }
    }
}

ProductRepository.cs

using OrderStore.Domain.Interfaces;
using OrderStore.Domain.Models;

namespace OrderStore.Repository
{
    class ProductRepository : GenericRepository<Product>, IProductRepository
    {
        public ProductRepository(ApplicationDbContext context) : base(context)
        {

        }
    }
}

存储库和 DbContext 注入

要使用 ApplicationDbContext 和存储库,我们需要能够将它们注入到 tp 依赖注入容器中。我们可以通过在存储库层创建一个扩展方法来做到这一点,如下所示

DependencyInjection.cs

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using OrderStore.Domain.Interfaces;

namespace OrderStore.Repository
{
    public static class DependencyInjection
    {
        public static IServiceCollection AddRepository(this IServiceCollection services)
        {
            services.AddTransient<IOrderRepository, OrderRepository>();
            services.AddTransient<IProductRepository, ProductRepository>();
            services.AddTransient<IUnitOfWork, UnitOfWork>();

            services.AddDbContext<ApplicationDbContext>(opt => opt
                .UseSqlServer("Server=DESKTOP-UUBJ14C\\SQLEXPRESS; Database=OrderDb;Trusted_Connection=True;"));
            return services;
        }
    }
}
我们现在可以在 API 的启动类中添加 AddRepository 。这使得在适当的层添加必要的依赖项变得非常容易。

Startup.cs

public IConfiguration Configuration { get; }

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    services.AddRepository();
    services.AddControllers();
    services.AddSwaggerGen(c =>
    {
        c.SwaggerDoc("v1", new OpenApiInfo { Title = "OrderStore", Version = "v1" });
    });
}

工作界面设计单元

我们代码的工作单元接口必须位于接口文件夹中,与上面相同。

IUnitOfWork.cs

using System;

namespace OrderStore.Domain.Interfaces
{
    public interface IUnitOfWork : IDisposable
    {
        IOrderRepository Orders { get; }
        IProductRepository Products { get; }
        int Complete();
    }
}

工作单元实施

工作单元将业务逻辑中涉及的所有实体保存到单个事务中,然后提交事务或回滚事务。工作单元接口的完整实现如下。

UnitOfWork.cs

using OrderStore.Domain.Interfaces;
using System;

namespace OrderStore.Repository
{
    public class UnitOfWork : IUnitOfWork
    {
        private readonly ApplicationDbContext _context;
        public IOrderRepository Orders { get; }
        public IProductRepository Products { get; }

        public UnitOfWork(ApplicationDbContext bookStoreDbContext,
            IOrderRepository booksRepository,
            IProductRepository catalogueRepository)
        {
            this._context = bookStoreDbContext;

            this.Orders = booksRepository;
            this.Products = catalogueRepository;
        }
        public int Complete()
        {
            return _context.SaveChanges();
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                _context.Dispose();
            }
        }
    }
}

模式执行

让我们添加迁移并更新我们的数据库。在包管理器控制台中依次运行以下命令,确保在执行命令之前切换到 OrderStore.Repository 

添加迁移’初始’

更新数据库

包装 API 控制器

我们现在可以将工作单元注入到我们的订单和产品控制器中。我们需要通过工作单元检索存储库及其相关业务逻辑。这将确保订单和产品将执行数据库操作。我在 Order Controller 中添加了两个 API 以从数据库中获取数据,并以相同的方式在 Product Controller 中添加了另外两个 API 以将数据发布和更新到数据库。

OrderController.cs

using Microsoft.AspNetCore.Mvc;
using OrderStore.Domain.Interfaces;
using OrderStore.Domain.Models;
using System.Threading.Tasks;

namespace OrderStore.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class OrderController : ControllerBase
    {
        private readonly IUnitOfWork _unitOfWork;
        public OrderController(IUnitOfWork unitOfWork)
        {
            _unitOfWork = unitOfWork;
        }

        // GET: api/<Books>
        [HttpGet(nameof(GetOrders))]
        public async Task<IActionResult> GetOrders() => Ok(await _unitOfWork.Orders.GetAll());

        [HttpGet(nameof(GetOrderByName))]
        public async Task<IActionResult> GetOrderByName([FromQuery] string Genre) => Ok(await _unitOfWork.Orders.GetOrdersByOrderName(Genre));

    }
}

ProductController.cs

using Microsoft.AspNetCore.Mvc;
using OrderStore.Domain.Interfaces;
using OrderStore.Domain.Models;

namespace OrderStore.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ProductController : ControllerBase
    {
        private readonly IUnitOfWork _unitOfWork;
        public ProductController(IUnitOfWork unitOfWork)
        {
            _unitOfWork = unitOfWork;
        }

        [HttpPost(nameof(CreateProduct))]
        public IActionResult CreateProduct(Product product)
        {
            var result = _unitOfWork.Products.Add(product);
            _unitOfWork.Complete();
            if (result is not null) return Ok("Product Created");
            else return BadRequest("Error in Creating the Product");
        }

        [HttpPut(nameof(UpdateProduct))]
        public IActionResult UpdateProduct(Product product)
        {
            _unitOfWork.Products.Update(product);
            _unitOfWork.Complete();
            return Ok("Product Updated");
        }
    }
}

项目结构

在 .Net 5 中使用依赖注入实现工作单元和存储库模式

一切就绪后,我们现在可以运行项目并启动 swagger 以在 Orders 端点提交 get 请求。让我们在 swagger URL 下查看我们所有的 API

在 .Net 5 中使用依赖注入实现工作单元和存储库模式

获取订单

在 .Net 5 中使用依赖注入实现工作单元和存储库模式

结论

上面的文章演示了使用 Repository Pattern 和 Unit of work 来确保干净的设计。

感谢您的阅读,请在评论部分告诉我您的问题、想法或反馈。我感谢您的反馈和鼓励。

您可以从此处的 GitHub 链接查看或下载源代码 。

保持学习 …!


慕源网 » 在 .Net 5 中使用依赖注入实现工作单元和存储库模式

常见问题FAQ

程序仅供学习研究,请勿用于非法用途,不得违反国家法律,否则后果自负,一切法律责任与本站无关。
请仔细阅读以上条款再购买,拍下即代表同意条款并遵守约定,谢谢大家支持理解!

发表评论

开通VIP 享更多特权,建议使用QQ登录