AspNet Core 3.1.0 + 領域驅動設計(DDD) + Dapper + MySql


AspNet Core 3.1.0 + 領域驅動設計(DDD) + Dapper + MySql


1、前提說明:

1、源碼參考

本文大部分代碼參照 https://www.cnblogs.com/royzshare/p/9522127.html以及其源碼 https://github.com/zdz72113/NETCore_BasicKnowledge.Examples結合分層經驗。原文使用的是Net Core 2.1

2、分層依賴

參考:https://www.cnblogs.com/Leo_wl/p/4343242.html。其中給出多種分層結構,選擇適合自己的方式。

2、搭建解決方案


AspNet Core 3.1.0 + 領域驅動設計(DDD) + Dapper + MySql


一、解決方案依賴的包

<code>Install-Package MySql.Data 8.0.19 
Install-Package Dapper 2.0.30
Install-Package Dapper.Contrib 2.0.30
Install-Package Microsoft.Extensions.Options 3.1.1/<code>

二、領域層


AspNet Core 3.1.0 + 領域驅動設計(DDD) + Dapper + MySql


1、實體接口,領域模型中所有實體對象繼承

<code>public interface IEntity { public string Id { get; } } /<code>

2、聚合根接口,聚合實體繼承此接口

<code>public interface IAggregateRoot : IEntity { }/<code>

3、包括領域事件,領域服務,和值對象等(省略)

4、示例:產品分類實體和產品實體

<code>[Table("Category")]
public class Category:IAggregateRoot
{
public int Id { get; set; }
public string Name { get; set; }
}
[Table("Product")]
public class Product:IAggregateRoot
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; }
public int CategoryId { get; set; }
public virtual Category Category { get; set; }
}/<code>

三、倉儲層

1、倉儲泛型接口,泛型參數接口 約束-聚合根

<code>public interface IRepository<taggregateroot> where TAggregateRoot : class, IAggregateRoot
{
Task<bool> AddAsync(TAggregateRoot entity);
Task<ienumerable>> GetAllAsync();
Task<taggregateroot> GetByIDAsync(int id);
Task<bool> DeleteAsync(int id);

Task<bool> UpdateAsync(TAggregateRoot entity);
}/<bool>/<bool>/<taggregateroot>/<ienumerable>/<bool>/<taggregateroot>/<code>

2、示例:定義產品分類和產品倉儲接口

<code>public interface ICategoryRepository : IRepository<category> { }
public interface IProductRepository : IRepository<product> { }/<product>/<category>/<code>

3、上下文IContext

提供對象列表的一切操作接口。在基礎層去實現操作。示例:DapperDBContext

<code>public interface IContext : IDisposable
{
bool IsTransactionStarted { get; }
void BeginTransaction();
void Commit();
void Rollback();
}/<code>

4、工作單元IUnitOfWork

統一工作單元,維護受業務事務影響的對象列表,並協調變化的寫入和併發問題的解決

<code>public interface IUnitOfWork : IDisposable
{
void SaveChanges();
}/<code>

定義工作單元工廠,創建工廠實例實現工作單元,關聯對象操作上下文。

<code> public interface IUnitOfWorkFactory
{
IUnitOfWork Create();
}/<code>

四、基礎層

1、實現IContext實例DapperDBContext和Dapper操作擴展Execute&Query

<code>public abstract class DapperDBContext : IContext
{
private IDbConnection _connection;
private IDbTransaction _transaction;
private int? _commandTimeout = null;
private readonly DapperDBContextOptions _options;
public bool IsTransactionStarted { get; private set; }
protected abstract IDbConnection CreateConnection(string connectionString);
public DapperDBContext(IOptions<dapperdbcontextoptions> optionsAccessor)
{
_options = optionsAccessor.Value;
_connection = CreateConnection(_options.Configuration);
_connection.Open();
DebugPrint("Connection started.");
}
private void DebugPrint(string message)
{
#if DEBUG
Debug.Print(">>> UnitOfWorkWithDapper - Thread {0}: {1}", Thread.CurrentThread.ManagedThreadId, message);
#endif
}
public void BeginTransaction()
{
if (IsTransactionStarted)
throw new InvalidOperationException("Transaction is already started.");
_transaction = _connection.BeginTransaction();
IsTransactionStarted = true;
DebugPrint("Transaction started.");
}
public void Commit()
{
if (!IsTransactionStarted)
throw new InvalidOperationException("No transaction started.");
_transaction.Commit();
_transaction = null;
IsTransactionStarted = false;
DebugPrint("Transaction committed.");
}
public void Dispose()
{
if (IsTransactionStarted)
Rollback();
_connection.Close();

_connection.Dispose();
_connection = null;
DebugPrint("Connection closed and disposed.");
}
public void Rollback()
{
if (!IsTransactionStarted)
throw new InvalidOperationException("No transaction started.");
_transaction.Rollback();
_transaction.Dispose();
_transaction = null;
IsTransactionStarted = false;
DebugPrint("Transaction rollbacked and disposed.");
}
#region Dapper Execute & Query
public async Task ExecuteAsync(string sql, object param = null, CommandType commandType = CommandType.Text)
{
return await _connection.ExecuteAsync(sql, param, _transaction, _commandTimeout, commandType);
}
public async Task<ienumerable>> QueryAsync(string sql, object param = null, CommandType commandType = CommandType.Text)
{
return await _connection.QueryAsync(sql, param, _transaction, _commandTimeout, commandType);
}
public async Task QueryFirstOrDefaultAsync(string sql, object param = null, CommandType commandType = CommandType.Text)
{
return await _connection.QueryFirstOrDefaultAsync(sql, param, _transaction, _commandTimeout, commandType);
}
public async Task<ienumerable>> QueryAsync<tfirst>(string sql, Func<tfirst> map, object param = null, string splitOn = "Id", CommandType commandType = CommandType.Text)
{
return await _connection.QueryAsync(sql, map, param, _transaction, true, splitOn, _commandTimeout, commandType);
}
#endregion Dapper Execute & Query
}/<tfirst>/<tfirst>/<ienumerable>
/<ienumerable>
/<dapperdbcontextoptions>/<code>

IOptions配置實例

<code>public class DapperDBContextOptions : IOptions<dapperdbcontextoptions>
{

public string Configuration { get; set; }
public DapperDBContextOptions Value
{
get { return this; }
}
} /<dapperdbcontextoptions>/<code>

服務擴展類:DapperDBContextServiceCollectionExtensions

<code>public static class DapperDBContextServiceCollectionExtensions
{
public static IServiceCollection AddDapperDBContext(this IServiceCollection services, Action<dapperdbcontextoptions> setupAction) where T : DapperDBContext
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
if (setupAction == null)
{
throw new ArgumentNullException(nameof(setupAction));
}
services.AddOptions();
services.Configure(setupAction);
services.AddScoped<dapperdbcontext>();
services.AddScoped<iunitofworkfactory>();
return services;
}
}/<iunitofworkfactory>/<dapperdbcontext>/<dapperdbcontextoptions>
/<code>

上下文配置的實現

<code>public class TestDBContext : DapperDBContext
{
public TestDBContext(IOptions<dapperdbcontextoptions> options) : base(options){}
protected override IDbConnection CreateConnection(string connectionString)
{
IDbConnection conn = new MySqlConnection(connectionString);
return conn;
}
}/<dapperdbcontextoptions>/<code>


2、數據持久化操作

倉儲實現

<code>public class UowProductRepository : IProductRepository
{
private readonly DapperDBContext _context;
public UowProductRepository(DapperDBContext context)
{
_context = context;
}

public async Task<product> GetByIDAsync(int id)
{
string sql = @"SELECT Id,Name,Quantity,Price,CategoryId FROM Product WHERE Id = @Id";
return await _context.QueryFirstOrDefaultAsync<product>(sql, new { Id = id });
}

public async Task<bool> AddAsync(Product prod)
{
string sql = @"INSERT INTO Product(Name,Quantity,Price,CategoryId) VALUES (@Name ,@Quantity,@Price @CategoryId)";
return await _context.ExecuteAsync(sql, prod) > 0;
}

public async Task<ienumerable>> GetAllAsync()
{
return await _context.QueryAsync<product>(@"SELECT Id,Name,Quantity,Price,CategoryId FROM Product");
}

public async Task<bool> DeleteAsync(int id)
{
string sql = @"DELETE FROM Product WHERE Id = @Id";
return await _context.ExecuteAsync(sql, new { Id = id }) > 0;
}

public async Task<bool> UpdateAsync(Product prod)
{
string sql = @"UPDATE Product SET Name = @Name,Quantity = @Quantity,Price= @Price ,CategoryId= @CategoryId WHERE Id = @Id";
return await _context.ExecuteAsync(sql, prod) > 0;
}
}/<bool>/<bool>/<product>/<ienumerable>/<bool>/<product>/<product>/<code>

工作單元實現數據提交持久化執行

<code>public class UnitOfWork : IUnitOfWork
{
private readonly IContext _context;
public UnitOfWork(IContext context)
{
_context = context;
_context.BeginTransaction();

}
public void SaveChanges()
{
if (!_context.IsTransactionStarted)
throw new InvalidOperationException("Transaction have already been commited or disposed.");

_context.Commit();
}

public void Dispose()
{
if (_context.IsTransactionStarted)
_context.Rollback();
}
}/<code>

五、應用層(服務層)

構建常用公共服務

<code>public interface IAppService<tentity> where TEntity:class,IEntity
{
Task<bool> AddAsync(TEntity entity);
Task<ienumerable>> GetAllAsync();
Task<tentity> GetByIDAsync(int id);
Task<bool> DeleteAsync(int id);
Task<bool> UpdateAsync(TEntity entity);
}/<bool>/<bool>/<tentity>/<ienumerable>/<bool>/<tentity>/<code>

產品服務和品種服務的定義

<code>public interface IProductService : IAppService<product> { }
public interface ICategoryService : IAppService<category> { }/<category>/<product>/<code>

實現方法

品種服務實現

<code>public class CategoryService : ICategoryService
{
private readonly IUnitOfWorkFactory _uowFactory;
private readonly ICategoryRepository _categoryRepository;
public CategoryService(IUnitOfWorkFactory uowFactory, ICategoryRepository categoryRepository)
{
_uowFactory = uowFactory;
_categoryRepository = categoryRepository;

}

public async Task<bool> AddAsync(Category entity)
{
using (var uow = _uowFactory.Create())
{
var result = await _categoryRepository.AddAsync(entity);
uow.SaveChanges();
return result;
}
}

public async Task<bool> DeleteAsync(int id)
{
using (var uow = _uowFactory.Create())
{
var result = await _categoryRepository.DeleteAsync(id);
uow.SaveChanges();
return result;
}
}

public async Task<ienumerable>> GetAllAsync()
{
return await _categoryRepository.GetAllAsync();
}

public async Task<category> GetByIDAsync(int id)
{
return await _categoryRepository.GetByIDAsync(id);
}

public async Task<bool> UpdateAsync(Category entity)
{
using (var uow = _uowFactory.Create())
{
var result = await _categoryRepository.UpdateAsync(entity);
uow.SaveChanges();
return result;
}
}
}/<bool>/<category>/<ienumerable>/<bool>/<bool>/<code>

產品服務實現

<code>public class ProductService : IProductService
{
private readonly IUnitOfWorkFactory _uowFactory;
private readonly IProductRepository _productRepository;
private readonly ICategoryRepository _categoryRepository;

public ProductService(IUnitOfWorkFactory uowFactory, IProductRepository productRepository, ICategoryRepository categoryRepository)
{
_uowFactory = uowFactory;
_productRepository = productRepository;
_categoryRepository = categoryRepository;
}

public async Task<bool> AddAsync(Product entity)
{
using (var uow = _uowFactory.Create())
{
var result = await _productRepository.AddAsync(entity);
uow.SaveChanges();
return result;
}
}

public async Task<bool> DeleteAsync(int id)
{
using (var uow = _uowFactory.Create())
{
var result = await _productRepository.DeleteAsync(id);
uow.SaveChanges();
return result;
}
}

public async Task<ienumerable>> GetAllAsync()
{
return await _productRepository.GetAllAsync();
}

public async Task<product> GetByIDAsync(int id)
{
return await _productRepository.GetByIDAsync(id);
}

public async Task<bool> UpdateAsync(Product entity)
{
using (var uow = _uowFactory.Create())
{
var result = await _productRepository.UpdateAsync(entity);
uow.SaveChanges();
return result;
}
}
}/<bool>/<product>/<ienumerable>/<bool>/<bool>/<code>

六、WebApi層代表的服務調用層

<code>[Route("api/[controller]")]
[ApiController]
public class ProductController : ControllerBase
{
private IProductService _productservice;
public ProductController(IProductService productservice)
{
_productservice = productservice;
}

[HttpGet("{id}")]
public async Task<iactionresult> Get(int id)
{
var data = await _productservice.GetByIDAsync(id);
return Ok(data);
}

[HttpPost]
public async Task<iactionresult> Post([FromBody] Product prod)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
await _productservice.AddAsync(prod);
return NoContent();
}

[HttpPut("{id}")]
public async Task<iactionresult> Put(int id, [FromBody] Product prod)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}

var model = await _productservice.GetByIDAsync(id);
model.Name = prod.Name;
model.Quantity = prod.Quantity;
model.Price = prod.Price;
await _productservice.UpdateAsync(model);

return NoContent();
}

[HttpDelete("{id}")]
public async Task<iactionresult> Delete(int id)
{
await _productservice.DeleteAsync(id);

return NoContent();
}
}/<iactionresult>/<iactionresult>/<iactionresult>/<iactionresult>/<code>

服務的注入:

通過注入的方式實現數據庫鏈接語句的配置

<code>public void ConfigureServices(IServiceCollection services)
{
////IUnitofWork是以工廠的形式創建的,不需要服務的注入
//SQL配置
services.AddDapperDBContext<testdbcontext>(options =>
{
options.Configuration = @"server=127.0.0.1;database=orange;uid=root;pwd=root;SslMode=none;";
});

//Service
services.AddScoped<iproductservice>();
services.AddScoped<icategoryservice>();

//Repository
services.AddScoped<iproductrepository>();
services.AddScoped<icategoryrepository>();

services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
services.AddControllers();
}/<icategoryrepository>/<iproductrepository>/<icategoryservice>/<iproductservice>/<testdbcontext>/<code>

七:SQL語句

<code>CREATE SCHEMA `orange` ;

CREATE TABLE `orange`.`category` (
`Id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(45) NOT NULL,
PRIMARY KEY (`Id`));

CREATE TABLE `orange`.`product` (
`Id` INT NOT NULL AUTO_INCREMENT,
`Name` VARCHAR(45) NOT NULL,
`Price` DECIMAL(19,2) NULL,
`Quantity` INT NULL,
`CategoryId` INT NOT NULL,
PRIMARY KEY (`Id`),

INDEX `fk_product_category_idx` (`CategoryId` ASC),
CONSTRAINT `fk_product_category`
FOREIGN KEY (`CategoryId`)
REFERENCES `orange`.`category` (`Id`)
ON DELETE CASCADE
ON UPDATE NO ACTION);

INSERT INTO `orange`.`category` (`Name`) VALUES("Phones");
INSERT INTO `orange`.`category` (`Name`) VALUES("Computers");

INSERT INTO `orange`.`product` (`Name`,`Price`,`Quantity`,`CategoryId`) VALUES("iPhone8",4999.99,10,1);
INSERT INTO `orange`.`product` (`Name`,`Price`,`Quantity`,`CategoryId`) VALUES("iPhone7",2999.99,10,1);
INSERT INTO `orange`.`product` (`Name`,`Price`,`Quantity`,`CategoryId`) VALUES("HP750",6000.00,5,2);
INSERT INTO `orange`.`product` (`Name`,`Price`,`Quantity`,`CategoryId`) VALUES("HP5000",12000.00,10,2);/<code>

八、調試


AspNet Core 3.1.0 + 領域驅動設計(DDD) + Dapper + MySql


AspNet Core 3.1.0 + 領域驅動設計(DDD) + Dapper + MySql


簡單的描述asp.net core 3.0 中關於領域驅動設計DDD相關概念結構的實現過程,同時結合Dapper簡單對象映射器。完全撇開EFCore的複雜映射。可能有部分人更願意使用Dapper.


後續將會集成Swagger + AutoMapper 等實戰用用到的內容。

下面有相關參考:

使用ASP.NET Core構建RESTful API的技術指南

https://mp.weixin.qq.com/s/s_3iI_8apahmr-DtJ7i9iQ


分享到:


相關文章: