• 重新整理 .net core 实践篇—————仓储层的具体实现[二十七]


    前言

    简单整理一下仓储层。

    正文

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

    /// <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,这个是领域设计的驱动。

  • 相关阅读:
    超详细教程2021新版oracle官网下载Windows JAVA-jdk11并安装配置(其他版本流程相同)
    个人总结
    6.15 团队项目心得
    五月团队项目收获
    八大排序算法读书笔记
    设计模式读书笔记3
    设计模式读书笔记2
    结对编程收获
    设计模式读书笔记
    UI-12组结对编程作业总结
  • 原文地址:https://www.cnblogs.com/aoximin/p/14906453.html
Copyright © 2020-2023  润新知