重新整理.netcore实践篇—————仓储层的具体实现[二十七](代码片段)

你永远想象不到,一个光鲜亮丽的Application,有多么 你永远想象不到,一个光鲜亮丽的Application,有多么肮脏的Code     2022-12-01     560

关键词:

前言

简单整理一下仓储层。

正文

在共享层的基础建设类库中:

/// <summary>
/// 泛型仓储接口
/// </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>
/// 泛型仓储接口
/// </summary>
/// <typeparam name="TEntity">实体类型</typeparam>
/// <typeparam name="TKey">主键Id类型</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);

IRepository 是定义了一个接口,表示要实现增删改查方法。

同样在该类库下,创建了对应的实现。

之所以在相同类库中建立实现的原因,就是因为没有必要分为两个类库。

以前我们写三层的时候分为IDAL 类库和 DAL 类库。IDAl 是接口层,DAL 是具体的实现。他们就称为DataAccessLayer层,也就是数据访问层。

然后用的时候发现一个问题,那就是数据库非常的稳定,哪家公司没事会去换数据库呢?

然后就把DAl类库和IDAL类库合并到DAl类库,然后把接口写在DAl类库,新建一个文件夹,叫做IDAl文件夹,里面放置接口。

如果到时候部分迁移到另外的数据库,又可以把接口移出来,新建类库进行重写这部分。

同样的现在微服务,每个应用都比较小,那么DAl可能就那么几个类,同样类中实现的方法也就那么几个,然后可能就把接口和类写在同一个cs里面。

当然这种是因为是数据库不会换,会有这种演变。如果是扩展性比较强的,比如依赖注入,那么还是要把接口和实现分开。

上面这个只是个人理解,如有错误望请指点。

实现如下:

/// <summary>
/// 泛型仓储抽象基类
/// </summary>
/// <typeparam name="TEntity">实体类型</typeparam>
/// <typeparam name="TDbContext">EFContext实例</typeparam>
public abstract class Repository<TEntity, TDbContext> : IRepository<TEntity> where TEntity : Entity, IAggregateRoot where TDbContext : EFContext

	protected virtual TDbContext DbContext  get; set; 

	public Repository(TDbContext dbContext)
	
		DbContext = dbContext;
	

	/// <summary>
	/// 工作单元
	/// 因为 EFContext 实现了 IUnitOfWork,所以这里直接返回 EFContext 的实例即可
	/// </summary>
	public IUnitOfWork UnitOfWork => DbContext;

	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 bool Remove(Entity entity)
	
		DbContext.Remove(entity);
		return true;
	

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



/// <summary>
/// 泛型仓储抽象基类
/// </summary>
/// <typeparam name="TEntity">实体类型</typeparam>
/// <typeparam name="TKey">主键Id类型</typeparam>
/// <typeparam name="TDbContext">EFContext实例</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 dbContext)
		: base(dbContext)
	
	

	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);
	

然后到了基础建设层,也就是具体实现层,我们需要注入模型与数据库的映射关系:

/// <summary>
/// EFContext具体实现
/// </summary>
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);
	

这里我随便找一个模型的应用配置看下,看下order的。

/// <summary>
/// 领域模型 Order 数据库映射配置
/// </summary>
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(c => c.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);
		);
	

定义了一些主键、表名、设置字段长度限制、导航属性。

对了,如果你们的数据库很稳定,且多个应用都用到了这些表,那么也可以将这些剥离到一个类库中共享。

因为我们的事务是工作单元模式,那么事务的处理是独立开来的,那么看下在基础建设层,事务的处理如下(这个在后面的使用中会具体介绍):

/// <summary>
/// 数据库上下文事务处理
/// </summary>
/// <typeparam name="TRequest"></typeparam>
/// <typeparam name="TResponse"></typeparam>
public class DomainContextTransactionBehavior<TRequest, TResponse> : TransactionBehavior<DomainContext, TRequest, TResponse>

	public DomainContextTransactionBehavior(DomainContext dbContext, ICapPublisher capBus, ILogger<DomainContextTransactionBehavior<TRequest, TResponse>> logger)
		: base(dbContext, capBus, logger)
	
	

具体的仓储实现类:

/// <summary>
/// Order 仓储实现类
/// </summary>
public class OrderRepository : Repository<Order, long, DomainContext>, IOrderRepository

	public OrderRepository(DomainContext context)
		: base(context)
	
	

然后我们就需要注册仓储服务和数据库服务:

// 注册 MySql 数据库上下文 
services.AddMySqlDomainContext(Configuration.GetValue<string>("MySql"));

// 注册 仓储服务      
services.AddRepositories();

显然这两个是扩展服务:

/// <summary>
/// 注册MySql服务
/// </summary>
/// <param name="services"></param>
/// <param name="connectionString"></param>
/// <returns></returns>
public static IServiceCollection AddMySqlDomainContext(this IServiceCollection services, string connectionString)

	return services.AddDomainContext(builder =>
	
		// package: Pomelo.EntityFrameworkCore.MySql
		builder.UseMySql(connectionString);
	);


/// <summary>
/// 注册仓储服务
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public static IServiceCollection AddRepositories(this IServiceCollection services)

	services.AddScoped<IOrderRepository, OrderRepository>();
	return services;

当我们启动的时候,如果数据库里面没有这个数据库,那么就会生成。

下一节,简单介绍一下Mediator,这个是领域设计的驱动。

重新整理.netcore实践篇—————应用分层[二十四](代码片段)

前言简单整理一下分层。正文应用程序分层,分为:1.领域模型层2.基础设施层3.应用层4.共享层共享层共享层一般包括下面几个类库。有一个Core的类库,比如说BLog.Core.这个类库用来,主要用来承载一些基础简单的类型,比如说一... 查看详情

重新整理.netcore实践篇—————中间件[十九](代码片段)

前言简单介绍一下.netcore的中间件。正文官方文档已经给出了中间件的概念图:和其密切相关的是下面这两个东西:IApplicationBuilder和RequestDelegate(HttpContextcontext)IApplicationBuilder:publicinterfaceIApplicationBuilderIServiceProviderApplicationService 查看详情

重新整理.netcore实践篇—————应用层[三十](代码片段)

前言简单介绍一下应用层。正文应用层用来做什么的呢?应用层用来做处理api请求的。[HttpPost]publicTask<long>CreateOrder([FromBody]CreateOrderVeiwModelviewModel) varmodel=viewModel.ToModel(); returnawaitorderService.CreateOrder(model); 查看详情

重新整理.netcore实践篇—————静态中间件[二十一](代码片段)

前言简单整理一下静态中间件。正文我们使用静态文件调用:app.UseStaticFiles();那么这个默认会将我们根目录下的wwwroot作为静态目录。这个就比较值得注意的,可能刚开始学.netcore的小伙伴,会直接把脚本写在更目录script这样是访... 查看详情

重新整理.netcore实践篇—————路由和终结点[二十三](代码片段)

前言简单整理一下路由和终节点。正文路由方式主要有两种:1.路由模板方式2.RouteAttribute方式路由约束:1.类型约束2.范围约束3.正则表达式4.是否必选5.自定义IRootConstaintURL生成1.LinKGenerator2.IUrlHelper先搭建一个swagger:services.AddSwaggerGen(... 查看详情

重新整理.netcore实践篇—————entity的定义[二十五](代码片段)

前言简单介绍一下实体模型的设计。正文前文提及了我们的应用分为:共享层基础设施层领域层应用层今天来介绍领域模型层。前文提及到领域模型在共享层有一个领域模型抽象类库。里面有这些类:先分别介绍一下这些类是做... 查看详情

重新整理.netcore实践篇—————微服务的桥梁eventbus[三十一](代码片段)

前言简单介绍一下EventBus.正文EventBus也就是集成事件,用于服务与服务之间的通信。比如说我们的订单处理事件,当订单处理完毕后,我们如果通过api马上去调用后续接口。比如说订单完成给用户通知的话,如果是大量订单,即... 查看详情

重新整理.netcore实践篇—————日志系统之结构化[十八](代码片段)

前言什么是结构化呢?结构化,就是将原本没有规律的东西进行有规律话。就比如我们学习数据结构,需要学习排序然后又要学习查询,说白了这就是一套,没有排序,谈如何查询是没有意义的,因为查询算法就是根据某种规律... 查看详情

重新整理.netcore实践篇—linux上排查问题实用工具[外篇]

前言介绍下面几个工具:Lldbcreatedumpdotnet-dumpdotnet-gcdumpdotnet-symbolProcdump该文的前置篇为:https://www.cnblogs.com/aoximin/p/16839812.html献给初学者,这篇就只介绍下看下日志和lldb,毕竟东西太多了。正文我以官网的例子作为演示:... 查看详情

abp(netcore)+easyui+efcore实现仓储管理系统——模块管理升级之上(六十一)(代码片段)

在前面的文章中我们已经将abp(netcore)+easyui+efcore实现的仓储管理系统升级到ABP7.3,今天我们对组织管理功能进行升级,并解决升级过后的一些问题。 Abp(netcore)+easyui+efcore实现仓储管理系统目录abp(netcore)+easyui+efcore实现仓储管理... 查看详情

abp(netcore)+easyui+efcore实现仓储管理系统——供应商管理升级之上(六十三)(代码片段)

...进行升级,这次的升级涉及到前端页面的一些问题。abp(netcore)+easyui+efcore实现仓储管理系统目录abp(netcore)+easyui+efcore实现仓储管理系统——ABP总体介绍(一)abp(netcore)+easyui+efcore实现仓储管理系统——解决方案介绍(二)abp(netcore)+easyu... 查看详情

asp.netcore中的规约模式(specificationpattern)——增强泛型仓储模式

...ification-pattern-in-aspnet-core/在本文中,我们将讨论在ASP.NETCore应用程序中实现规约模式以及它如何增强现有的泛型仓储模式。我们将从头开始构建具有泛型仓储模式、EntityFrameworkCore的ASP.NETCoreWebAPI, 查看详情

.netcore杂记

...解了.netcore设计理念和设计思想(纯属跟人理解)。再此整理了之前写的一些学习笔记,后续也会把新的学习新的加上。1..netcore跨平台实践2.asp.netcore使用EF7CodeFirst创建数据库,同时使用命令创建数据库 查看详情

一个lumen多层拆分手记

...der(服务商)依赖注入,还有Action(动作)、Repositories(仓储)等... 先讲一下仓储吧,一般JAVA和C#在M层和C层之间会有一个仓储层做连接,C层直接调用仓储层,然后由仓储层调用model层的方法,实现MC层的解耦,今天有幸在PH... 查看详情

.netcore实践篇————网关

参考技术A简单整理一下网关。在介绍网关之前,介绍一下BFF,BFF全称是BackendForFrontend,它负责认证授权,服务聚合,目标是为前端提供服务。说的通透一点,就是有没有见过这种服务。上述就是buff通过代理其他服务来让前端访... 查看详情

度小满金融大数据架构实践

分享嘉宾:赵辉度小满金融架构师,编辑整理:蒋雯娟厦门大学嘉庚学院,出品平台:DataFunTalk导读:大数据架构在金融场景下面临着诸多挑战,从架构上而言,业务对数据加工、存储和使用的全链... 查看详情

asp.netcore分布式项目实战整理identityserver4mvc授权consent功能实现(代码片段)

...前言由于之前的博客都是基于其他的博客进行开发,现在重新整理一下方便以后后期使用与学习新建IdentityServer4服务端服务端也就是提供服务,如Q 查看详情

abp领域层-仓储

一、仓储的定义:在领域层和数据映射层的中介,使用类似集合的接口来存取领域对象。实际上,仓储被用于领域对象再数据库上的操作。              一般对不同的实体会创建... 查看详情