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


分享到:


相關文章: