

基础领域驱动DDD框架搭建,配合sqlsugar基础架构搭建。使用仓储。.net使用Autofac实现自动注入。通过特性封装事务,特性实现工作单元

一:创建一个WebApi项目随便取名一个,比如TeacherCertificationAPI
效果如下:
顺便在appsettings.json中配置一下连接字符串,以及是否输出sql的配置,配置好之后如下:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"writeSQl": true,
"DbConfig": {
"DbType": 0,
// 配置数据库连接
"ConnectionString": ".....",
"IsAutoCloseConnection": true
}
}
二:创建好应用层
应用层 :其实就是对应用户操作的用例,不包含领域逻辑。这一层是领域层的门户,负责调用领域层或基础设施层,起到一个编排作用。只有应用逻辑,没有领域逻辑。否则就是事务脚本模式,业务逻辑散落在各个服务中,给后期维护造成困难,如:分配客服后,执行事务保存,调用日志组件记录异常,分配客服的领域逻辑封装在领域服务中。
应用层面更多关注横切面的内容:
1.业务参数的检查
2.异常记录
3.事务
4.防腐层(如:DTO转为领域模型)
先随便创建好一个测试的应用服务的接口和类
三:创建好领域层
领域层,包含领域实体、领域逻辑。实体不光有数据,还有行为逻辑。依据信息专家式:“谁有这些数据,谁就负责数据的维护”。否则就会造成模型贫血,领域逻辑散落在各个应用服务中,导致代码重复,腐败。如果遇到两个实体对象的协调,那么则使用领域服务处理。如:账户实体A给账户实体B转账,这个转账过程放在哪个实体里面都不合适,那么就放在领域服务中处理。领域服务是没有状态的,只有行为。
更多概念的解释可以参考:https://www.tnblog.net/aojiancc2/article/details/8582
这里的Tasks是创建的一个测试的领域对象,BaseProperty是基础的通用的对象字段,比如创建人,创建时间,更新人什么的,代码如下:
namespace TeacherCertification.Domain
{
/// <summary>
/// 基础字段
/// </summary>
public class BaseProperty
{
public int TenantID { get; set; }
/// <summary>
/// 创建人
/// </summary>
public string CreateID { get; set; }
public string CreateName { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreateTime { get; set; }
/// <summary>
/// 修改人
/// </summary>
[SugarColumn(IsNullable = true)]
public string? UpdateID { get; set; }
public string UpdateName { get; set; }
/// <summary>
/// 修改时间
/// </summary>
[SugarColumn(IsNullable = true)]
public DateTime? UpdateTime { get; set; }
/// <summary>
/// 设置新增 和 修改 的基础信息
/// </summary>
/// <param name="AccID"></param>
/// <param name="Name"></param>
/// <param name="TID"></param>
/// <param name="HasUpdate"></param>
public void SettingVal(string AccID, string Name, int TID, bool HasUpdate)
{
TenantID = TID;
CreateID = AccID;
CreateName = Name;
CreateTime = DateTime.Now;
if (HasUpdate)
{
UpdateID = AccID;
UpdateName = Name;
UpdateTime = DateTime.Now;
}
}
}
}
Tasks对象也贴一下吧:
using SqlSugar;
namespace TeacherCertification.Domain.TestGrowing
{
[SugarTable("tasks")]
public class Tasks : BaseProperty
{
/// <summary>
///
/// </summary>
[Newtonsoft.Json.JsonConverter(typeof(ValueToStringConverter))]
[SugarColumn(IsPrimaryKey = true)]
public long ID { get; set; }
/// <summary>
/// 任务名称
/// </summary>
public System.String TaskName { get; set; }
/// <summary>
/// 任务类别ID
/// </summary>
public System.Int32 TaskCatID { get; set; }
/// <summary>
/// 任务类型:1/图文2/成绩3/资料4/图文成绩
/// </summary>
public System.Int32 TaskType { get; set; }
/// <summary>
/// 任务归属0/集团任务1/校区任务
/// </summary>
public System.Int32 TaskTP { get; set; }
/// <summary>
/// 是否展示任务标题
/// </summary>
public bool HasTitle { get; set; }
/// <summary>
/// 标题名称
/// </summary>
public System.String? Title { get; set; }
/// <summary>
/// 任务得分
/// </summary>
public System.Int32 Score { get; set; }
/// <summary>
/// 状态:0/禁用1/启用99/删除
/// </summary>
public System.Int32 Status { get; set; }
/// <summary>
/// 校区ID
/// </summary>
public System.String? SchoolID { get; set; }
/// <summary>
/// 模版ID
/// </summary>
public int? TempID { get; set; }
/// <summary>
/// 0/待审核 1/审核通过 2/驳回
/// </summary>
public int CheckStatus { get; set; }
/// <summary>
/// 驳回备注
/// </summary>
public System.String? REJRemark { get; set; }
public System.Int32 OrderNum { get; set; }
}
}
这里的设计其实并没有非常严格的按照领域启动DDD来分层,我们这里的领域实体,其实也是数据库实体,当然我们实体里边也可以写逻辑,按照面向对象的思想来,完全不影响,SQLSugar也是直接支持值对象的操作,所以我们这里把领域对象和数据库实体一起也是科学的,还有一点就是其实我们中小型公司一般都是业务驱动,业务一来马上就要开动,经常就要吹进度,根本不会给那么多时间来设计,所以分层太多太细会很影响前期的进度,所以只能简化一些分层,像ABP的DDD分层也是这样的,淡化了一些DDD思想的,像很多其他开源的快速开发框架也是这样的。
sqlsugar 的值对象文档:https://www.donet5.com/Home/Doc?typeId=2576
关于领域模型的可以看看这篇文章:https://www.tnblog.net/aojiancc2/article/details/7304
这个是前面写的使用ABP VNext搭建领域驱动架构的如下:
https://www.tnblog.net/aojiancc2/article/details/7268
因为领域层的对象要作为SqlSugar中的数据库映射实体,所以在这一层也要引入一下SqlSugar
<ItemGroup>
<PackageReference Include="SqlSugarCore" Version="5.1.4.169" />
</ItemGroup>
四:创建好基础设施层的数据库持久化层
基础设施层,包含仓储,第三方接口实现,公共基础方法。一个仓储对应一个聚合,一个仓储就是一个事务单元,同时也屏蔽了技术的复杂性,让服务更关心领域本身。如一个Order聚合,就应该只实现一个OrderRepository,OrderRepository内部实现Order,Orderltem的事务保存。仓储不应该只有增删改查的简单操作,还应该有更丰满的操作。比如检查工单是否存在,而不是返回null,再在服务层再去做是否为null的判断。仓储除了单个聚合事务的操作,有时需要跨多个聚合的事务操作,这时可以使用UnitOfWork模式,注入DBContext到仓储层,在单个Request请求上下文公用同一个DBContext,以达到多个聚合的事务保存。
添加相关依赖,SqlSugar以及获取配置的
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.169" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
</ItemGroup>
创建好SqlSugar注入的初始化类
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using SqlSugar;
using TeacherCertification.ORMCore.Model;
namespace TeacherCertification.ORMCore.SqlSugarExtensions
{
public static class SqlsugarInit
{
public static void AddSqlsugar(this IServiceCollection services)
{
if (services == null) throw new ArgumentNullException(nameof(services));
var configuration = BuildConfiguration();
// 获取单个配置(获取是否写入sql得配置)
bool writeSQl = configuration.GetValue<bool>("writeSQl");
// 把多个连接对象注入服务,这里必须采用Scope,因为有事务操作
services.AddScoped<ISqlSugarClient>(o =>
{
// 获取数据库相关的配置
var dbConfigSection = configuration.GetSection("DbConfig");
var dbConfig = new DbConfigModel();
dbConfigSection.Bind(dbConfig); // 将配置绑定到对象
SqlSugarScope sqlSugar = new SqlSugarScope(new ConnectionConfig()
{
DbType = DbType.MySql,
ConnectionString = dbConfig.ConnectionString,
IsAutoCloseConnection = true,
},
db =>
{
var config = db.CurrentConnectionConfig;
//单例参数配置,所有上下文生效
db.Aop.OnLogExecuting = (sql, pars) =>
{
if (writeSQl)
{
if (sql.StartsWith("SELECT", StringComparison.OrdinalIgnoreCase))
Console.ForegroundColor = ConsoleColor.Green;
if (sql.StartsWith("UPDATE", StringComparison.OrdinalIgnoreCase) || sql.StartsWith("INSERT", StringComparison.OrdinalIgnoreCase))
Console.ForegroundColor = ConsoleColor.White;
if (sql.StartsWith("DELETE", StringComparison.OrdinalIgnoreCase))
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine("【" + DateTime.Now + "——执行SQL】\r\n" + UtilMethods.GetSqlString(config.DbType, sql, pars) + "\r\n");
}
};
});
return sqlSugar;
});
}
/// <summary>
/// 用于构建获取主项目配置文件的
/// (也可以在主项目注入好,在进行获取配置,这里只在这里获取就直接这样写了)
/// </summary>
/// <returns></returns>
private static IConfigurationRoot BuildConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Path.Combine(Directory.GetCurrentDirectory(), "../TeacherCertificationAPI/"))
.AddJsonFile("appsettings.json", optional: true);
return builder.Build();
}
}
}
SqlSugar的基础引用参考官网:https://www.donet5.com/home/Doc?typeId=1181
这里使用的是IOC注入的
那个获取配置的实体类也贴一下
using SqlSugar;
namespace TeacherCertification.ORMCore.Model
{
public class DbConfigModel
{
public DbType DbType { get; set; }
public string? ConnectionString { get; set; }
public bool IsAutoCloseConnection { get; set; }
public bool EnableCache { get; set; }
/// <summary>
/// 全局禁用读写分离
/// </summary>
public bool IsDisableMasterSlaveSeparation { get; set; }
/// <summary>
/// 从库配置
/// </summary>
public List<SlaveConnectionConfig> SlaveConnectionConfigs { get; set; }
}
}
五:基础架构搭建好了,补充其他基础的内容
添加好各层之间的引用关系
主项目添加应用层和基础设施层的数据库持久化层的引用
应用层添加领域层的引用
基础设置层添加领域层的引用
在主项目的Program注入好相关依赖
核心代码
builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// 注入SqlSugar的实例
builder.Services.AddSqlsugar();
// 注入自己定义的一个服务
builder.Services.AddTransient<ITasksAppService, TasksAppService>();
全部代码也贴一下:
using TeacherCertification.Application.TestGrowing;
using TeacherCertification.ORMCore.SqlSugarExtensions;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// 注入SqlSugar的实例
builder.Services.AddSqlsugar();
// 注入自己定义的一个服务
builder.Services.AddTransient<ITasksAppService, TasksAppService>();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseAuthorization();
app.MapControllers();
app.Run();
在创建的测试领域服务中添加测试的方法,就可以使用了
这里通过依赖注入获取到SqlSugar的对象就可以使用SqlSugar进行数据库查询了
using SqlSugar;
using TeacherCertification.Domain.TestGrowing;
namespace TeacherCertification.Application.TestGrowing
{
public class TasksAppService : ITasksAppService
{
// 通过依赖注入获取到SqlSugar的对象
private readonly ISqlSugarClient _db;
public TasksAppService(ISqlSugarClient db)
{
_db = db;
}
public void GetTasks()
{
List<Tasks> tasks = _db.Queryable<Tasks>().Take(10).ToList();
}
}
}
当然现在这一层直接使用到了SqlSugar的对象,所以也要添加一下引用:
<ItemGroup>
<PackageReference Include="SqlSugarCore" Version="5.1.4.169" />
</ItemGroup>
截止到目前的示例代码下载
链接: https://pan.baidu.com/s/1WQQ1YGaccF8WJ29jyuOP1g?pwd=qc4g 提取码: qc4g 复制这段内容后打开百度网盘手机App,操作更方便哦
六:改造SqlSugar的使用,使用仓储
这个是sqlsugar官方关于仓储的文档:https://www.donet5.com/Home/Doc?typeId=1228
先按照官方文档那样直接使用sqlsugar的仓储非常简单
在领域层创建一个Repository类,代码结构如下:
在Repositor类中加入如下代码:
using SqlSugar;
namespace TeacherCertification.Domain
{
public class Repository<T> : SimpleClient<T> where T : class, new()
{
public Repository(ISqlSugarClient db)
{
base.Context = db;
}
/// <summary>
/// 扩展方法,自带方法不能满足的时候可以添加新方法
/// </summary>
/// <returns></returns>
public List<T> CommQuery(string json)
{
return null;
}
}
}
然后在主项目的Program.cs去加入仓储类的注入:
builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// 注入SqlSugar的仓储
builder.Services.AddScoped(typeof(Repository<>));
builder.Services.AddSqlsugar();
builder.Services.AddTransient<ITasksAppService, TasksAppService>();
然后就可以在应用层去使用了:
using TeacherCertification.Domain;
using TeacherCertification.Domain.TestGrowing;
namespace TeacherCertification.Application.TestGrowing
{
public class TasksAppService : ITasksAppService
{
// 通过依赖注入获取到SqlSugar仓储的实例
private readonly Repository<Tasks> _taskRepository;
public TasksAppService(Repository<Tasks> taskRepository)
{
_taskRepository = taskRepository;
}
public void GetTasks()
{
List<Tasks> tasks = _taskRepository.GetList();
/*
* .Context可以拿到db对象,但是按照领域驱动的思想这里不应该直接去依赖与具体的持久化层了,
* 而是依赖于仓储这种更抽象的东西,当然很多公司每天加班业务都做不完,为了方便也是很多直接在应用层就依赖持久化层了
*/
List<Tasks> tasks2 = _taskRepository.Context.Queryable<Tasks>().Take(10).ToList();
}
}
}
直接使用sqlsugar仓储示例代码下载
链接: https://pan.baidu.com/s/1SK_LlWg4-CGB_OHCXfpw6w?pwd=c6e8 提取码: c6e8 复制这段内容后打开百度网盘手机App,操作更方便哦
改造一下sqlsugar仓储的使用,让仓储依赖于接口,而不是具体的实现
sqlsugar官网仓储的使用是直接注入的一个实现类,这样领域层就直接依赖于持久层的实现了,其实更好的做法是依赖于更抽象的接口,而不是具体的实现。仓储的实现应该放到数据库层去,应用层应该是依赖于抽象,而不关心具体的持久化实现,对这一层来说持久化用什么技术细节实现都可以,
先把领域层的仓储实现改成接口
代码结构如下:
具体的代码如下,代码非常简单,还要依赖sqlsugar的仓储接口:
using SqlSugar;
namespace TeacherCertification.Domain
{
/// <summary>
/// 仓储的实现其实应该放到数据库层去,这一层应该是依赖于抽象,
/// 而不依赖于具体的实现。但是这里是用的SqlSugar作为基础设施层的数据库层,
/// 所以需要依赖于SqlSugar封装的一些接口。
/// 正确的依赖关系应该是基础设置层依赖于领域层。
/// 我们分层最主要的作用就是避免领域层遭受技术语言的入侵,像数据库这种持久化的具体细节应该是
/// 基础设置层的事情
/// </summary>
public interface IRepository<T> : ISugarRepository, ISimpleClient<T> where T : class, new()
{
}
}
然后仓储的具体实现就要放到基础设置层中的持久化层去了
代码如下:
using SqlSugar;
using TeacherCertification.Domain;
namespace TeacherCertification.ORMCore
{
public class Repository<T> : SimpleClient<T>, IRepository<T> where T : class, new()
{
public Repository(ISqlSugarClient db)
{
base.Context = db;
}
}
}
然后在主项目的Program.cs注入一下仓储接口与仓储实现就可以使用了
在主项目的Program.cs注入一下仓储接口与仓储实现:
builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// 注入SqlSugar的仓储
builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
builder.Services.AddSqlsugar();
builder.Services.AddTransient<ITasksAppService, TasksAppService>();
然后就可以使用这个仓储接口了:
using TeacherCertification.Domain;
using TeacherCertification.Domain.TestGrowing;
namespace TeacherCertification.Application.TestGrowing
{
public class TasksAppService : ITasksAppService
{
// 通过依赖注入获取到SqlSugar仓储的实例
private readonly IRepository<Tasks> _taskRepository;
public TasksAppService(IRepository<Tasks> taskRepository)
{
_taskRepository = taskRepository;
}
public void GetTasks()
{
List<Tasks> tasks = _taskRepository.GetList();
/*
* .Context可以拿到db对象,但是按照领域驱动的思想这里不应该直接去依赖与具体的持久化层了,
* 而是依赖于仓储这种更抽象的东西,当然很多公司每天加班业务都做不完,为了方便也是很多直接在应用层就依赖持久化层了
*/
List<Tasks> tasks2 = _taskRepository.Context.Queryable<Tasks>().Take(10).ToList();
}
}
}
改造仓储后示例代码下载
链接: https://pan.baidu.com/s/1WVUWR8rEpTL5ptuJQph1Ww?pwd=bwhq 提取码: bwhq 复制这段内容后打开百度网盘手机App,操作更方便哦
当封装的仓储不满足使用的时候其实应该单独在提供一个操作的接口出来
namespace TeacherCertification.Domain.TestGrowing
{
/// <summary>
/// 封装的基础仓储不满足的时候其实应该走单独在提供一个
/// 操作的接口出来的,这样可以让应用层不直接依赖于持久化层的
/// 实现细节,都是依赖于抽象的,具体的细节应该是在基础设置层的持久化实现里边,
/// 只是我们平时为了方便直接就在应用层拿到持久化实现的对象去直接操作数据库了。
/// </summary>
public interface ITasksRepository
{
}
}
七:改造手动依赖注入,使用Autofac实现自动注入
在主项目中加入Autofac的引用
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="9.0.0" />
Autofac.Extensions.DependencyInjection已经包含了Autofac基础库的引用
然后在需要使用注意注入的层里边添加一个获取程序集名称的类与方法(非必须可以省略这个,直接写死)
using System.Reflection;
namespace TeacherCertification.Application
{
public static class ApplicationServiceAutofac
{
/// <summary>
/// 获取程序集名称
/// </summary>
/// <returns></returns>
public static string? GetAssemblyName()
{
return Assembly.GetExecutingAssembly().GetName().Name;
}
}
}
创建一个自动注入的逻辑类AutofacModuleRegister
加入自动注入的逻辑:
using Autofac;
using System.Reflection;
using TeacherCertification.Application;
namespace TeacherCertificationAPI.IOC
{
/// <summary>
/// Autofac自动注入实现
/// </summary>
public class AutofacModuleRegister : Autofac.Module
{
/// <summary>
///
/// </summary>
/// <param name="builder"></param>
protected override void Load(ContainerBuilder builder)
{
//自动注入Application层的(也可以直接写死名称就不用在需要注入的层去写获取名称的方法了)
string? appAssemblyNam = ApplicationServiceAutofac.GetAssemblyName();
if (appAssemblyNam != null)
{
var assemblysServices = Assembly.Load(appAssemblyNam);
builder.RegisterAssemblyTypes(assemblysServices)
.Where(x => x.Name.EndsWith("AppService", StringComparison.OrdinalIgnoreCase))
.AsSelf()//注入自身
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
//.PropertiesAutowired(); // 开启属性注入;
}
}
}
}
然后在Program.cs中去写好相关注入即可
builder.Host
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureContainer<ContainerBuilder>(builder =>
{
builder.RegisterModule(new AutofacModuleRegister());
});
好了,完成上述的步骤,应用层的接口和实现就不需要自动注入了,其他层的注入原理一样。但是要注意位置不要写到builder.Build()
下面去了
.net使用Autofac实现自动注入示例代码下载
链接: https://pan.baidu.com/s/1cyh_zgRHxT8_pNQEJslZtw?pwd=xqyw 提取码: xqyw 复制这段内容后打开百度网盘手机App,操作更方便哦
八:AutoMapper的使用
一:添加AutoMapper依赖
<PackageReference Include="AutoMapper" Version="13.0.1" />
或者使用命令安装:
Install-Package AutoMapper
在需要的层都添加一下这个依赖,比如我这里在主项目和应用层都添加了一下
二:创建 AutoMapper 配置
你需要创建一个配置类来定义源对象和目标对象之间的映射关系,放到应用层里边去
using AutoMapper;
using TeacherCertification.Application.TestGrowing.Dto;
using TeacherCertification.Domain.TestGrowing;
namespace TeacherCertification.Application
{
public class ApplicationAutoMapperProfile : Profile
{
public ApplicationAutoMapperProfile()
{
CreateMap<TaskDto, Tasks>().ReverseMap();
CreateMap<UpdateTaskDto, Tasks>().ReverseMap();
CreateMap<MenuDto, Menu>().ReverseMap();
CreateMap<UpdateMenuDto, Menu>().ReverseMap();
CreateMap<MenuMetaDto, Menu>().ReverseMap();
// 单向映射
CreateMap<Menu,MenuMetaDto>();
// 指定了字段映射的
//CreateMap<Menu, MenuMetaDto>()
// .ForMember(dest => dest.IsHide, opt => opt.MapFrom(src => src.IsHide))
// .ForMember(dest => dest.IsAffix, opt => opt.MapFrom(src => src.IsAffix));
}
}
}
三:注册 AutoMapper
在 Program.cs 文件中需要注册AutoMapper
// 注册 AutoMapper
builder.Services.AddAutoMapper(typeof(ApplicationAutoMapperProfile));
四:使用 AutoMapper
经过上面的步骤后就可以在各层使用AutoMapper了
using AutoMapper;
using TeacherCertification.Application.TestGrowing.Dto;
using TeacherCertification.Domain;
using TeacherCertification.Domain.TestGrowing;
namespace TeacherCertification.Application.TestGrowing
{
public class TasksAppService : ITasksAppService
{
// 通过依赖注入获取到SqlSugar仓储的实例
private readonly IRepository<Tasks> _taskRepository;
private readonly IMapper _mapper;
public TasksAppService(IRepository<Tasks> taskRepository, IMapper mapper)
{
_taskRepository = taskRepository;
_mapper = mapper;
}
public List<TaskDto> GetTasks()
{
List<Tasks> tasks = _taskRepository.GetList();
List<TaskDto> result = _mapper.Map<List<Tasks>, List<TaskDto>>(tasks);
return result;
}
}
}
五:使用 AutoMapper的示例代码下载
链接: https://pan.baidu.com/s/17gdcviPT-VVINIn8IMT4Vg?pwd=ga9r 提取码: ga9r 复制这段内容后打开百度网盘手机App,操作更方便哦
其实ABP VNext里边这种模块化的封装更科学一点,这个是前面写的使用ABP VNext搭建领域驱动架构里边关于AutoMapper的使用
参考:https://www.tnblog.net/aojiancc2/article/details/7268#%E5%BA%94%E7%94%A8%E5%B1%82%E5%88%9B%E5%BB%BA%E4%B8%8EAutoMapper%E4%BD%BF%E7%94%A8
九:改造应用层AutoMapper的使用方式,放到应用层的统一父类中
在自动注入的逻辑中开放属性注入
在应用层中创建一个名为ApplicationService的抽象类,用来作为应用层的统一父类
需要用一个统一的抽象类来统一管理应用服务,可以把应用服务中公共的内容写入这个抽象类中,比如AutoMapper的使用,写到父类中去就不用在每个应用服务都去获取一IC了。
代码如下:
主要就是通过属性注入拿到了IServiceProvider,然后通过IServiceProvider拿到了AutoMapper的实例
public abstract class ApplicationService
{
public IServiceProvider? ServiceProvider { get; set; }
protected IMapper ObjectMapper
{
get
{
IMapper? mapper = ServiceProvider?.GetService<IMapper>();
return mapper;
}
}
}
然后应用服务继承ApplicationService即可直接使用AutoMapper了
public class TasksAppService : ApplicationService,ITasksAppService
{
// 通过依赖注入获取到SqlSugar仓储的实例
private readonly IRepository<Tasks> _taskRepository;
public TasksAppService(IRepository<Tasks> taskRepository)
{
_taskRepository = taskRepository;
}
public List<TaskDto> GetTasks()
{
List<Tasks> tasks = _taskRepository.GetList();
List<TaskDto> result = ObjectMapper.Map<List<Tasks>, List<TaskDto>>(tasks);
return result;
}
}
十:通过特性封装事务,特性实现工作单元,通用统一的事务封装
添加一个名称叫TransactionFilter
的过滤器:
加入如下代码:
/// <summary>
/// Sqlsugar全局事务,IActionFilter
/// </summary>
public class TransactionFilter : IActionFilter
{
ISqlSugarClient _db;//你也可以换EF CORE对象 或者ADO对象都行
/// <summary>
///
/// </summary>
/// <param name="db"></param>
public TransactionFilter(ISqlSugarClient db)//(ISqlSugarClient)需要IOC注入处理事务的对象
{
_db = db;
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
public void OnActionExecuting(ActionExecutingContext context)
{
_db.AsTenant().BeginTran();//接口要加.AsTenant()
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
public void OnActionExecuted(ActionExecutedContext context)
{
if (context.Exception == null)
{
_db.AsTenant().CommitTran();
}
else
{
_db.AsTenant().RollbackTran();
//_db.AsTenant().RollBack();
}
}
}
在Program.cs中去注入一下封装的过滤器
//注入事务对象
builder.Services.AddScoped<TransactionFilter>();
然后在接口加上过滤器的使用就可以了
[ServiceFilter(typeof(TransactionFilter))]//加上这行就可以用了
[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
return null
}
这样请求接口里边所有调用的方法,所有使用sqlsugar操作数据库的地方都会用到事务了(期间不要再去重新手动开启或者提交事务了,且上下文对象要保证是一个)
原理:
原理其实非常的简单,因为正常的操作在一次请求中sqlsugar的上下文对象都是同一个,所有这个请求这一次请求所有操作数据库都是在同一次事务中的,当然前提是期间不要再去重新手动开启或者提交事务了,且上下文对象要保证是一个(只要是正常的操作都会是同一个上下文对象的,因为都是从依赖注入中获取的,且依赖注入生命周期是这样设置的)
欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739。有需要软件开发,或者学习软件技术的朋友可以和我联系~(Q:815170684)
忘掉过去式
剑哥流批