前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >.NET Core开发实战(第29课:定义仓储:使用EF Core实现仓储层)--学习笔记

.NET Core开发实战(第29课:定义仓储:使用EF Core实现仓储层)--学习笔记

作者头像
郑子铭
发布于 2021-01-13 07:31:44
发布于 2021-01-13 07:31:44
2.4K00
代码可运行
举报
运行总次数:0
代码可运行

29 | 定义仓储:使用EF Core实现仓储层

首先定义仓储层的接口,以及仓储层实现的基类,抽象类

仓储层的接口

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
namespace GeekTime.Infrastructure.Core
{
    /// <summary>
    /// 包含普通实体的仓储
    /// 约束 TEntity 必须是继承 Entity 的基类,必须实现聚合根 IAggregateRoot
    /// 也就是说仓储里面存储的对象必须是一个聚合根对象
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    public interface IRepository<TEntity> where TEntity : Entity, IAggregateRoot
    {
        IUnitOfWork UnitOfWork { get; }
        TEntity Add(TEntity entity);
        Task<TEntity> AddAsync(TEntity entity, CancellationToken cancellationToken = default);
        TEntity Update(TEntity entity);
        Task<TEntity> UpdateAsync(TEntity entity, CancellationToken cancellationToken = default);
        bool Remove(Entity entity);// 由于没有指定主键,只能根据当前实体进行删除操作
        Task<bool> RemoveAsync(Entity entity);
    }

    /// <summary>
    /// 包含指定主键的类型的实体的仓储
    /// 继承了上面的接口 IRepository<TEntity>,也就是说拥有了上面定义的所有方法
    /// 另外一个,它实现了几个跟 Id 相关的操作的方法
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    /// <typeparam name="TKey"></typeparam>
    public interface IRepository<TEntity, TKey> : IRepository<TEntity> where TEntity : Entity<TKey>, IAggregateRoot
    {
        bool Delete(TKey id);
        Task<bool> DeleteAsync(TKey id, CancellationToken cancellationToken = default);
        TEntity Get(TKey id);
        Task<TEntity> GetAsync(TKey id, CancellationToken cancellationToken = default);
    }
}

具体抽象类的实现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
namespace GeekTime.Infrastructure.Core
{
    /// <summary>
    /// 定义普通实体的仓储
    /// 定义约束 TDbContext 必须是 EFContext,也就是仓储必须依赖于 EFContext 及其子类
    /// 将来就可以把自己定义的比如 DomainContext 作为泛型参数传入 Repository,就可以很快捷地定义出来自己的仓储
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    /// <typeparam name="TDbContext"></typeparam>
    public abstract class Repository<TEntity, TDbContext> : IRepository<TEntity> where TEntity : Entity, IAggregateRoot where TDbContext : EFContext
    {
        // 具体实现需要依赖 DbContext
        protected virtual TDbContext DbContext { get; set; }

        public Repository(TDbContext context)
        {
            this.DbContext = context;
        }
        public virtual IUnitOfWork UnitOfWork => DbContext;// 因为 DbContext, EFContext 实际上实现了 IUnitOfWork,所以直接返回

        // 下面这些方法都是 EntityFramework 提供的能力,所以就能通过简单的几行代码来实现基本的仓储操作

        public virtual TEntity Add(TEntity entity)
        {
            return DbContext.Add(entity).Entity;
        }

        public virtual Task<TEntity> AddAsync(TEntity entity, CancellationToken cancellationToken = default)
        {
            return Task.FromResult(Add(entity));
        }

        public virtual TEntity Update(TEntity entity)
        {
            return DbContext.Update(entity).Entity;
        }

        public virtual Task<TEntity> UpdateAsync(TEntity entity, CancellationToken cancellationToken = default)
        {
            return Task.FromResult(Update(entity));
        }

        public virtual bool Remove(Entity entity)
        {
            DbContext.Remove(entity);
            return true;
        }

        public virtual Task<bool> RemoveAsync(Entity entity)
        {
            return Task.FromResult(Remove(entity));
        }
    }

    /// <summary>
    /// 定义主键的实体的仓储
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    /// <typeparam name="TKey"></typeparam>
    /// <typeparam name="TDbContext"></typeparam>
    public abstract class Repository<TEntity, TKey, TDbContext> : Repository<TEntity, TDbContext>, IRepository<TEntity, TKey> where TEntity : Entity<TKey>, IAggregateRoot where TDbContext : EFContext
    {
        public Repository(TDbContext context) : base(context)
        {
        }

        /// <summary>
        /// 根据 Id 从 DbContext 获取 Entity,然后再 Remove
        /// 这样的好处是可以跟踪对象的状态
        /// 坏处是任意的删除都需要先去数据库里面做查询
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public virtual bool Delete(TKey id)
        {
            var entity = DbContext.Find<TEntity>(id);
            if (entity == null)
            {
                return false;
            }
            DbContext.Remove(entity);
            return true;
        }

        public virtual async Task<bool> DeleteAsync(TKey id, CancellationToken cancellationToken = default)
        {
            var entity = await DbContext.FindAsync<TEntity>(id, cancellationToken);
            if (entity == null)
            {
                return false;
            }
            DbContext.Remove(entity);
            return true;
        }

        public virtual TEntity Get(TKey id)
        {
            return DbContext.Find<TEntity>(id);
        }

        public virtual async Task<TEntity> GetAsync(TKey id, CancellationToken cancellationToken = default)
        {
            return await DbContext.FindAsync<TEntity>(id, cancellationToken);
        }
    }



}

实现自己的 DbContext

DomainContext

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
namespace GeekTime.Infrastructure
{
    public class DomainContext : EFContext
    {
        public DomainContext(DbContextOptions options, IMediator mediator, ICapPublisher capBus) : base(options, mediator, capBus)
        {
        }

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

        public DbSet<User> Users { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            #region 注册领域模型与数据库的映射关系
            modelBuilder.ApplyConfiguration(new OrderEntityTypeConfiguration());
            modelBuilder.ApplyConfiguration(new UserEntityTypeConfiguration());
            #endregion
            base.OnModelCreating(modelBuilder);
        }
    }
}

映射关系,针对每一个领域模型创建一个 EntityTypeConfiguration

OrderEntityTypeConfiguration

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
namespace GeekTime.Infrastructure.EntityConfigurations
{
    class OrderEntityTypeConfiguration : IEntityTypeConfiguration<Order>
    {
        public void Configure(EntityTypeBuilder<Order> builder)
        {
            // 定义主键
            builder.HasKey(p => p.Id);
            //builder.ToTable("order");
            //builder.Property(p => p.UserId).HasMaxLength(20);
            //builder.Property(p => p.UserName).HasMaxLength(30);

            // 定义导航属性
            builder.OwnsOne(o => o.Address, a =>
                {
                    a.WithOwner();
                    //a.Property(p => p.City).HasMaxLength(20);
                    //a.Property(p => p.Street).HasMaxLength(50);
                    //a.Property(p => p.ZipCode).HasMaxLength(10);
                });
        }
    }
}

UserEntityTypeConfiguration

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
namespace GeekTime.Infrastructure.EntityConfigurations
{
    class UserEntityTypeConfiguration : IEntityTypeConfiguration<User>
    {
        public void Configure(EntityTypeBuilder<User> builder)
        {
            builder.HasKey(p => p.Id);
        }
    }
}

事务处理

要实现对 DomainContext 的事务处理的话,仅仅需要创建一个类 DomainContextTransactionBehavior

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
namespace GeekTime.Infrastructure
{
    public class DomainContextTransactionBehavior<TRequest, TResponse> : TransactionBehavior<DomainContext, TRequest, TResponse>
    {
        public DomainContextTransactionBehavior(DomainContext dbContext, ICapPublisher capBus, ILogger<DomainContextTransactionBehavior<TRequest, TResponse>> logger) : base(dbContext, capBus, logger)
        {
        }
    }
}

为了演示效果,在应用程序启动时,添加一行代码

Startup

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 这一行代码的作用是创建一个 Scope,在这个范围内创建 DomainContext
using (var scope = app.ApplicationServices.CreateScope())
{
    var dc = scope.ServiceProvider.GetService<DomainContext>();

    // 确定数据库已经创建,如果数据库没有创建,这个时候会执行数据库的自动创建过程,根据模型创建数据库
    dc.Database.EnsureCreated();
}

数据库的注册部分

ServiceCollectionExtensions

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/// <summary>
/// 这个定义就是将连接字符串配置到 dDomainContext
/// </summary>
/// <param name="services"></param>
/// <param name="connectionString"></param>
/// <returns></returns>
public static IServiceCollection AddMySqlDomainContext(this IServiceCollection services, string connectionString)
{
    return services.AddDomainContext(builder =>
    {
        builder.UseMySql(connectionString);
    });
}

这一行代码的调用位置是在 ConfigureServices 里面

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 从配置中获取字符串
services.AddMySqlDomainContext(Configuration.GetValue<string>("Mysql"));

启动程序,运行过程中 EF 框架会根据定义的实体映射关系生成数据库,可在 Mysql 数据库中查看生成结果

接着丰富一下 Order 的映射关系

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
namespace GeekTime.Infrastructure.EntityConfigurations
{
    class OrderEntityTypeConfiguration : IEntityTypeConfiguration<Order>
    {
        public void Configure(EntityTypeBuilder<Order> builder)
        {
            // 定义主键
            builder.HasKey(p => p.Id);
            builder.ToTable("order");// 修改表名为 order,不带 s
            builder.Property(p => p.UserId).HasMaxLength(20);// 修改字段长度
            builder.Property(p => p.UserName).HasMaxLength(30);

            // 定义导航属性
            // OwnsOne 的方式可以将 Address 这个值类型作为同一个表的字段来设置
            builder.OwnsOne(o => o.Address, a =>
                {
                    a.WithOwner();
                    a.Property(p => p.City).HasMaxLength(20);
                    a.Property(p => p.Street).HasMaxLength(50);
                    a.Property(p => p.ZipCode).HasMaxLength(10);
                });
        }
    }
}

启动程序,可以看到数据库修改结果

这说明可以在仓储层定义领域模型与数据库的映射关系,这个映射关系可以组织为一个目录,为每一个领域模型设置一个类型来定义,并且这个过程是强类型的,这样的结构,便于后期维护

另外仓储层的话,定义了一个 IOrderRepository,仅仅实现了 IRepository 泛型接口,引进 Order,由于 Order 实际上有一个主键是 long,所以这里把主键类型也传给 IRepository

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
namespace GeekTime.Infrastructure.Repositories
{
    public interface IOrderRepository : IRepository<Order, long>
    {

    }
}

Order

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Order : Entity<long>, IAggregateRoot

这样子,Order 的仓储就定义完毕

那么 Order 仓储的实现也非常简单,仅仅需要继承 Repository,把 Order,long,DomainContext 传入泛型 Repository 即可,这里还实现了 IOrderRepository

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
namespace GeekTime.Infrastructure.Repositories
{
    public class OrderRepository : Repository<Order, long, DomainContext>, IOrderRepository
    {
        public OrderRepository(DomainContext context) : base(context)
        {
        }
    }
}

通过这样简单的继承,可以复用之前定义的代码,快速实现仓储层的定义

可以通过代码提升看到仓储层是有 Add,Update,Remove,Delete 方法,还有 UnitOfWork 的属性

这样一来就完成了仓储层的定义,可以看到仓储层的代码非常的薄,仅仅包含了一些接口的定义和类的继承,需要自定义一些方法的时候,可以在仓储层定义一些特殊方法,比如 AddABC 等特殊的逻辑都可以在这里去实现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
namespace GeekTime.Infrastructure.Repositories
{
    public class OrderRepository : Repository<Order, long, DomainContext>, IOrderRepository
    {
        public OrderRepository(DomainContext context) : base(context)
        {
        }
    }
    
    public void AddABC()
    {
        
    }
}

另外一个在组织领域模型和数据库的关系的时候,可以很清晰的看到,是在 EntityConfiguration 这个目录下面,为每一个模型定义一个映射类,当领域模型越来越复杂,数据库的结构越来越复杂的时候,这样的组织结构会非常的清晰

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-03-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DotNet NB 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
EF Core 实现读写分离的最佳方案
公司之前使用Ado.net和Dapper进行数据访问层的操作, 进行读写分离也比较简单, 只要使用对应的数据库连接字符串即可. 而最近要迁移到新系统中,新系统使用.net core和EF Core进行数据访问. 所以趁着假期拿出一两天时间研究了一下如何EF Core进行读写分离.
梁规晓
2019/10/10
2.3K0
.NET EF Core(Entity Framework Core)
1、Entity Framework Core(EF Core)是微软官方的ORM框架。优点:功能强大、官方支持、生产效率高、力求屏蔽底层数据库差异;缺点:复杂、上手门槛高、不熟悉EFCore的话可能会进坑。 2、Dapper。优点:简单,N分钟即可上手,行为可预期性强;缺点:生产效率低,需要处理底层数据库差异。 3、EF Core是 模型驱动 (Model-Driven)的开发思想,Dapper是 数据库驱动(DataBase-Driven)的开发思想的。没有优劣,只有比较。 4、性能: Dapper等≠性能高;EF Core≠性能差。 5、EF Core是官方推荐、推进的框架,尽量屏蔽底层数据库差异,.NET开发者必须熟悉,根据的项目情况再决定用哪个。
鱼找水需要时间
2024/03/23
5470
.NET EF Core(Entity Framework Core)
.NET Core开发实战(第26课:工程结构概览:定义应用分层及依赖关系)--学习笔记
源码链接: https://github.com/witskeeper/geektime/tree/master/microservices
郑子铭
2021/01/13
5290
.NET Core开发实战(第26课:工程结构概览:定义应用分层及依赖关系)--学习笔记
.NET Core开发实战(第27课:定义Entity:区分领域模型的内在逻辑和外在行为)--学习笔记
通常情况下实体只有一个 ID,但是也不排除存在多个 ID 的情况,所以这里的接口 IEntity 定义实现为多个 ID 的情况,而 IEntity 表示实体只有一个 Id
郑子铭
2021/01/13
4630
.NET Core实战项目之CMS 第十二章 开发篇-Dapper封装CURD及仓储代码生成器实现
本篇我将带着大家一起来对Dapper进行下封装并实现基本的增删改查、分页操作的同步异步方法的实现(已实现MSSQL,MySql,PgSQL)。同时我们再实现一下仓储层的代码生成器,这样的话,我们只需要结合业务来实现具体的业务部分的代码就可以了,可以大大减少我们重复而又繁琐的增删改查操作,多留点时间给生活充充电(不会偷懒的程序员不是一位好爸爸/好老公/好男朋友)。如果您觉得我的实现过程有所不妥的话,您可以在评论区留言,或者加入我们的千人.Net Core实战项目交流群637326624交流。另外如果您觉得我的文章对您有所帮助的话希望给个推荐以示支持。项目的源代码我会托管在GayHub上,地址在文章末尾会给出,自认为代码写的很工整,注释也很全,你应该能看懂
依乐祝
2018/12/27
9790
FreeSql.Repository (一)什么是仓储
欢迎来到《FreeSql.Repository 仓储模式》系列文档,完整文档请前往 wiki 中心:https://github.com/dotnetcore/FreeSql/wiki
huofo
2022/03/18
6220
efcore使用ShardingCore实现分表分库下的多租户
本期主角:ShardingCore 一款ef-core下高性能、轻量级针对分表分库读写分离的解决方案,具有零依赖、零学习成本、零业务代码入侵
落寞的鱼丶
2022/02/21
1.6K0
登录注册的小项目对比.Net Core与 .Net Framework的一些区别
UserSys.IServices:主要有实体和对实体的配置,还有对实体的操作接口
用户9184480
2024/12/19
550
登录注册的小项目对比.Net Core与 .Net Framework的一些区别
.NET Core开发实战(第30课:领域事件:提升业务内聚,实现模块解耦)--学习笔记
这是一个空接口,它只是标记出来某一个对象是否是领域事件,INotification 也是一个空接口,它是 MediatR 框架的一个接口,是用来实现事件传递用的
郑子铭
2021/01/13
6120
初识ABP vNext(11):聚合根、仓储、领域服务、应用服务、Blob储存
在前两节中介绍了ABP模块开发的基本步骤,试着实现了一个简单的文件管理模块;功能很简单,就是基于本地文件系统来完成文件的读写操作,数据也并没有保存到数据库,所以之前只简单使用了应用服务,并没有用到领域层。而在DDD中领域层是非常重要的一层,其中包含了实体,聚合根,领域服务,仓储等等,复杂的业务逻辑也应该在领域层来实现。本篇来完善一下文件管理模块,将文件记录保存到数据库,并使用ABP BLOB系统来完成文件的存储。
xhznl
2020/09/28
1.9K0
在 ASP.NET Core 项目中使用 MediatR 实现中介者模式
   最近有在看 DDD 的相关资料以及微软的 eShopOnContainers 这个项目中基于 DDD 的架构设计,在 Ordering 这个示例服务中,可以看到各层之间的代码调用与我们之前传统的调用方式似乎差异很大,整个项目各个层之间的代码全部是通过注入 IMediator 进行调用的,F12 查看源码后可以看到该接口是属于 MediatR 这个组件的。既然要照葫芦画瓢,那我们就先来了解下如何在 ASP.NET Core 项目中使用 MediatR。
程序员宇说
2019/11/20
9900
在 ASP.NET Core 项目中使用 MediatR 实现中介者模式
UnitOfWork知多少
Unit Of Work模式,由马丁大叔提出,是一种数据访问模式。UOW模式的作用是在业务用例的操作中跟踪对象的所有更改(增加、删除和更新),并将所有更改的对象保存在其维护的列表中。在业务用例的终点,通过事务,一次性提交所有更改,以确保数据的完整性和有效性。总而言之,UOW协调这些对象的持久化及并发问题。
圣杰
2019/09/12
2.4K0
一款EF Core下高性能、轻量级针对分表分库读写分离的解决方案
今天大姚给大家分享一款EF Core下高性能、轻量级针对分表分库读写分离的解决方案,开源(Apache License)的EF Core拓展程序包:ShardingCore。
追逐时光者
2024/07/06
2000
一款EF Core下高性能、轻量级针对分表分库读写分离的解决方案
.NET Core开发实战(第28课:工作单元模式(UnitOfWork):管理好你的事务)--学习笔记
这两个方法的区别是:一个是返回的 int 是指我们影响的数据条数,另外一个返回 bool 表示我们保存是否成功,本质上这两个方法达到的效果是相同的
郑子铭
2021/01/13
3.5K0
【愚公系列】2023年02月 .NET/C#知识点-使用控制台手搭webapi框架
WebAPI是一种协议,用于允许网络应用程序(如浏览器)与网络服务器(如Web服务器)之间进行通信。它可以用于处理数据,访问数据库,处理图像和视频,以及进行其他高级功能。
愚公搬代码
2023/03/16
1.5K0
【愚公系列】2023年02月 .NET/C#知识点-使用控制台手搭webapi框架
DDD领域驱动设计 (C# 整理自“老张的哲学”)
设计:这里指架构分层,即应该如何分层?领域逻辑写在哪?与持久化如何交互?如何协调多对象领域逻辑?如何实现逻辑与数据存储解耦等
明志德道
2023/10/21
2K0
DDD领域驱动设计 (C# 整理自“老张的哲学”)
Repository个人实践
  最近,有空了,想着把之前一些乱七八糟的小项目给整理一下,尤其是涉及到Repository、UoW几处。为此,专门查阅了博客园中几个大神 关于Repository的实践,到最后都感觉依然莫衷一是,于是感觉这玩意儿不能深究,自己还是紧扣Martin老爷子关于Repository及UoW的核心定义,自己实践核心概念就是了,其他的都不重要了。
guokun
2020/09/03
1K0
Repository个人实践
使用.net core ABP和Angular模板构建博客管理系统(创建后端服务)
如何创建.net core ABP和Angular模板可以参考我的这篇文章:http://blog.csdn.net/yiershan1314/article/details/78219322 创
易兒善
2018/08/21
6190
使用.net core ABP和Angular模板构建博客管理系统(创建后端服务)
.Net 从零开始构建一个框架之基本实体结构与基本仓储构建
本系列文章将介绍如何在.Net框架下,从零开始搭建一个完成CRUD的Framework,该Framework将具备以下功能,基本实体结构(基于DDD)、基本仓储结构、模块加载系统、工作单元、事件总线(EventBus,具有事件溯源的功能)、以及依赖注入管理系统.
郑小超.
2019/05/25
7690
.NET 云原生架构师训练营(模块二 基础巩固 EF Core 介绍)--学习笔记
一个工作单元在一个事务范围内保留所有对数据库的变更,在这个工作单元结束的时候一次性提交所有改动到数据库
郑子铭
2020/12/27
9600
.NET 云原生架构师训练营(模块二 基础巩固 EF Core 介绍)--学习笔记
推荐阅读
EF Core 实现读写分离的最佳方案
2.3K0
.NET EF Core(Entity Framework Core)
5470
.NET Core开发实战(第26课:工程结构概览:定义应用分层及依赖关系)--学习笔记
5290
.NET Core开发实战(第27课:定义Entity:区分领域模型的内在逻辑和外在行为)--学习笔记
4630
.NET Core实战项目之CMS 第十二章 开发篇-Dapper封装CURD及仓储代码生成器实现
9790
FreeSql.Repository (一)什么是仓储
6220
efcore使用ShardingCore实现分表分库下的多租户
1.6K0
登录注册的小项目对比.Net Core与 .Net Framework的一些区别
550
.NET Core开发实战(第30课:领域事件:提升业务内聚,实现模块解耦)--学习笔记
6120
初识ABP vNext(11):聚合根、仓储、领域服务、应用服务、Blob储存
1.9K0
在 ASP.NET Core 项目中使用 MediatR 实现中介者模式
9900
UnitOfWork知多少
2.4K0
一款EF Core下高性能、轻量级针对分表分库读写分离的解决方案
2000
.NET Core开发实战(第28课:工作单元模式(UnitOfWork):管理好你的事务)--学习笔记
3.5K0
【愚公系列】2023年02月 .NET/C#知识点-使用控制台手搭webapi框架
1.5K0
DDD领域驱动设计 (C# 整理自“老张的哲学”)
2K0
Repository个人实践
1K0
使用.net core ABP和Angular模板构建博客管理系统(创建后端服务)
6190
.Net 从零开始构建一个框架之基本实体结构与基本仓储构建
7690
.NET 云原生架构师训练营(模块二 基础巩固 EF Core 介绍)--学习笔记
9600
相关推荐
EF Core 实现读写分离的最佳方案
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文