重新整理 .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,這個是領域設計的驅動。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM