应无所住,而生其心
排名
1
文章
858
粉丝
112
评论
163
net core webapi post传递参数
庸人 : 确实坑哈,我也是下班好了好几次,发现后台传递对象是可以的,但...
百度编辑器自定义模板
庸人 : 我建议换个编辑器,因为现在百度富文本已经停止维护了,用tinymec...
ICP备案 :渝ICP备18016597号-1
网站信息:2018-2025TNBLOG.NET
技术交流:群号656732739
联系我们:contact@tnblog.net
公网安备:50010702506256
欢迎加群交流技术

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

1483人阅读 2024/10/25 22:40 总访问:5169437 评论:1 收藏:1 手机
分类: 软件架构

一:创建一个WebApi项目随便取名一个,比如TeacherCertificationAPI

效果如下:

顺便在appsettings.json中配置一下连接字符串,以及是否输出sql的配置,配置好之后如下:

  1. {
  2. "Logging": {
  3. "LogLevel": {
  4. "Default": "Information",
  5. "Microsoft.AspNetCore": "Warning"
  6. }
  7. },
  8. "AllowedHosts": "*",
  9. "writeSQl": true,
  10. "DbConfig": {
  11. "DbType": 0,
  12. // 配置数据库连接
  13. "ConnectionString": ".....",
  14. "IsAutoCloseConnection": true
  15. }
  16. }

二:创建好应用层


应用层 :其实就是对应用户操作的用例,不包含领域逻辑。这一层是领域层的门户,负责调用领域层或基础设施层,起到一个编排作用。只有应用逻辑,没有领域逻辑。否则就是事务脚本模式,业务逻辑散落在各个服务中,给后期维护造成困难,如:分配客服后,执行事务保存,调用日志组件记录异常,分配客服的领域逻辑封装在领域服务中。
应用层面更多关注横切面的内容:
1.业务参数的检查
2.异常记录
3.事务
4.防腐层(如:DTO转为领域模型)

先随便创建好一个测试的应用服务的接口和类

三:创建好领域层


领域层,包含领域实体、领域逻辑。实体不光有数据,还有行为逻辑。依据信息专家式:“谁有这些数据,谁就负责数据的维护”。否则就会造成模型贫血,领域逻辑散落在各个应用服务中,导致代码重复,腐败。如果遇到两个实体对象的协调,那么则使用领域服务处理。如:账户实体A给账户实体B转账,这个转账过程放在哪个实体里面都不合适,那么就放在领域服务中处理。领域服务是没有状态的,只有行为。

更多概念的解释可以参考:https://www.tnblog.net/aojiancc2/article/details/8582

这里的Tasks是创建的一个测试的领域对象,BaseProperty是基础的通用的对象字段,比如创建人,创建时间,更新人什么的,代码如下:

  1. namespace TeacherCertification.Domain
  2. {
  3. /// <summary>
  4. /// 基础字段
  5. /// </summary>
  6. public class BaseProperty
  7. {
  8. public int TenantID { get; set; }
  9. /// <summary>
  10. /// 创建人
  11. /// </summary>
  12. public string CreateID { get; set; }
  13. public string CreateName { get; set; }
  14. /// <summary>
  15. /// 创建时间
  16. /// </summary>
  17. public DateTime CreateTime { get; set; }
  18. /// <summary>
  19. /// 修改人
  20. /// </summary>
  21. [SugarColumn(IsNullable = true)]
  22. public string? UpdateID { get; set; }
  23. public string UpdateName { get; set; }
  24. /// <summary>
  25. /// 修改时间
  26. /// </summary>
  27. [SugarColumn(IsNullable = true)]
  28. public DateTime? UpdateTime { get; set; }
  29. /// <summary>
  30. /// 设置新增 和 修改 的基础信息
  31. /// </summary>
  32. /// <param name="AccID"></param>
  33. /// <param name="Name"></param>
  34. /// <param name="TID"></param>
  35. /// <param name="HasUpdate"></param>
  36. public void SettingVal(string AccID, string Name, int TID, bool HasUpdate)
  37. {
  38. TenantID = TID;
  39. CreateID = AccID;
  40. CreateName = Name;
  41. CreateTime = DateTime.Now;
  42. if (HasUpdate)
  43. {
  44. UpdateID = AccID;
  45. UpdateName = Name;
  46. UpdateTime = DateTime.Now;
  47. }
  48. }
  49. }
  50. }

Tasks对象也贴一下吧:

  1. using SqlSugar;
  2. namespace TeacherCertification.Domain.TestGrowing
  3. {
  4. [SugarTable("tasks")]
  5. public class Tasks : BaseProperty
  6. {
  7. /// <summary>
  8. ///
  9. /// </summary>
  10. [Newtonsoft.Json.JsonConverter(typeof(ValueToStringConverter))]
  11. [SugarColumn(IsPrimaryKey = true)]
  12. public long ID { get; set; }
  13. /// <summary>
  14. /// 任务名称
  15. /// </summary>
  16. public System.String TaskName { get; set; }
  17. /// <summary>
  18. /// 任务类别ID
  19. /// </summary>
  20. public System.Int32 TaskCatID { get; set; }
  21. /// <summary>
  22. /// 任务类型:1/图文2/成绩3/资料4/图文成绩
  23. /// </summary>
  24. public System.Int32 TaskType { get; set; }
  25. /// <summary>
  26. /// 任务归属0/集团任务1/校区任务
  27. /// </summary>
  28. public System.Int32 TaskTP { get; set; }
  29. /// <summary>
  30. /// 是否展示任务标题
  31. /// </summary>
  32. public bool HasTitle { get; set; }
  33. /// <summary>
  34. /// 标题名称
  35. /// </summary>
  36. public System.String? Title { get; set; }
  37. /// <summary>
  38. /// 任务得分
  39. /// </summary>
  40. public System.Int32 Score { get; set; }
  41. /// <summary>
  42. /// 状态:0/禁用1/启用99/删除
  43. /// </summary>
  44. public System.Int32 Status { get; set; }
  45. /// <summary>
  46. /// 校区ID
  47. /// </summary>
  48. public System.String? SchoolID { get; set; }
  49. /// <summary>
  50. /// 模版ID
  51. /// </summary>
  52. public int? TempID { get; set; }
  53. /// <summary>
  54. /// 0/待审核 1/审核通过 2/驳回
  55. /// </summary>
  56. public int CheckStatus { get; set; }
  57. /// <summary>
  58. /// 驳回备注
  59. /// </summary>
  60. public System.String? REJRemark { get; set; }
  61. public System.Int32 OrderNum { get; set; }
  62. }
  63. }

这里的设计其实并没有非常严格的按照领域启动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

  1. <ItemGroup>
  2. <PackageReference Include="SqlSugarCore" Version="5.1.4.169" />
  3. </ItemGroup>

四:创建好基础设施层的数据库持久化层


基础设施层,包含仓储,第三方接口实现,公共基础方法。一个仓储对应一个聚合,一个仓储就是一个事务单元,同时也屏蔽了技术的复杂性,让服务更关心领域本身。如一个Order聚合,就应该只实现一个OrderRepository,OrderRepository内部实现Order,Orderltem的事务保存。仓储不应该只有增删改查的简单操作,还应该有更丰满的操作。比如检查工单是否存在,而不是返回null,再在服务层再去做是否为null的判断。仓储除了单个聚合事务的操作,有时需要跨多个聚合的事务操作,这时可以使用UnitOfWork模式,注入DBContext到仓储层,在单个Request请求上下文公用同一个DBContext,以达到多个聚合的事务保存。

添加相关依赖,SqlSugar以及获取配置的

  1. <ItemGroup>
  2. <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
  3. <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" />
  4. <PackageReference Include="SqlSugarCore" Version="5.1.4.169" />
  5. <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
  6. </ItemGroup>

创建好SqlSugar注入的初始化类

  1. using Microsoft.Extensions.Configuration;
  2. using Microsoft.Extensions.DependencyInjection;
  3. using SqlSugar;
  4. using TeacherCertification.ORMCore.Model;
  5. namespace TeacherCertification.ORMCore.SqlSugarExtensions
  6. {
  7. public static class SqlsugarInit
  8. {
  9. public static void AddSqlsugar(this IServiceCollection services)
  10. {
  11. if (services == null) throw new ArgumentNullException(nameof(services));
  12. var configuration = BuildConfiguration();
  13. // 获取单个配置(获取是否写入sql得配置)
  14. bool writeSQl = configuration.GetValue<bool>("writeSQl");
  15. // 把多个连接对象注入服务,这里必须采用Scope,因为有事务操作
  16. services.AddScoped<ISqlSugarClient>(o =>
  17. {
  18. // 获取数据库相关的配置
  19. var dbConfigSection = configuration.GetSection("DbConfig");
  20. var dbConfig = new DbConfigModel();
  21. dbConfigSection.Bind(dbConfig); // 将配置绑定到对象
  22. SqlSugarScope sqlSugar = new SqlSugarScope(new ConnectionConfig()
  23. {
  24. DbType = DbType.MySql,
  25. ConnectionString = dbConfig.ConnectionString,
  26. IsAutoCloseConnection = true,
  27. },
  28. db =>
  29. {
  30. var config = db.CurrentConnectionConfig;
  31. //单例参数配置,所有上下文生效
  32. db.Aop.OnLogExecuting = (sql, pars) =>
  33. {
  34. if (writeSQl)
  35. {
  36. if (sql.StartsWith("SELECT", StringComparison.OrdinalIgnoreCase))
  37. Console.ForegroundColor = ConsoleColor.Green;
  38. if (sql.StartsWith("UPDATE", StringComparison.OrdinalIgnoreCase) || sql.StartsWith("INSERT", StringComparison.OrdinalIgnoreCase))
  39. Console.ForegroundColor = ConsoleColor.White;
  40. if (sql.StartsWith("DELETE", StringComparison.OrdinalIgnoreCase))
  41. Console.ForegroundColor = ConsoleColor.Blue;
  42. Console.WriteLine("【" + DateTime.Now + "——执行SQL】\r\n" + UtilMethods.GetSqlString(config.DbType, sql, pars) + "\r\n");
  43. }
  44. };
  45. });
  46. return sqlSugar;
  47. });
  48. }
  49. /// <summary>
  50. /// 用于构建获取主项目配置文件的
  51. /// (也可以在主项目注入好,在进行获取配置,这里只在这里获取就直接这样写了)
  52. /// </summary>
  53. /// <returns></returns>
  54. private static IConfigurationRoot BuildConfiguration()
  55. {
  56. var builder = new ConfigurationBuilder()
  57. .SetBasePath(Path.Combine(Directory.GetCurrentDirectory(), "../TeacherCertificationAPI/"))
  58. .AddJsonFile("appsettings.json", optional: true);
  59. return builder.Build();
  60. }
  61. }
  62. }

SqlSugar的基础引用参考官网:https://www.donet5.com/home/Doc?typeId=1181

这里使用的是IOC注入的

那个获取配置的实体类也贴一下

  1. using SqlSugar;
  2. namespace TeacherCertification.ORMCore.Model
  3. {
  4. public class DbConfigModel
  5. {
  6. public DbType DbType { get; set; }
  7. public string? ConnectionString { get; set; }
  8. public bool IsAutoCloseConnection { get; set; }
  9. public bool EnableCache { get; set; }
  10. /// <summary>
  11. /// 全局禁用读写分离
  12. /// </summary>
  13. public bool IsDisableMasterSlaveSeparation { get; set; }
  14. /// <summary>
  15. /// 从库配置
  16. /// </summary>
  17. public List<SlaveConnectionConfig> SlaveConnectionConfigs { get; set; }
  18. }
  19. }

五:基础架构搭建好了,补充其他基础的内容

添加好各层之间的引用关系

主项目添加应用层和基础设施层的数据库持久化层的引用
应用层添加领域层的引用
基础设置层添加领域层的引用

在主项目的Program注入好相关依赖

核心代码

  1. builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
  2. // 注入SqlSugar的实例
  3. builder.Services.AddSqlsugar();
  4. // 注入自己定义的一个服务
  5. builder.Services.AddTransient<ITasksAppService, TasksAppService>();

全部代码也贴一下:

  1. using TeacherCertification.Application.TestGrowing;
  2. using TeacherCertification.ORMCore.SqlSugarExtensions;
  3. var builder = WebApplication.CreateBuilder(args);
  4. builder.Services.AddControllers();
  5. builder.Services.AddEndpointsApiExplorer();
  6. builder.Services.AddSwaggerGen();
  7. builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
  8. // 注入SqlSugar的实例
  9. builder.Services.AddSqlsugar();
  10. // 注入自己定义的一个服务
  11. builder.Services.AddTransient<ITasksAppService, TasksAppService>();
  12. var app = builder.Build();
  13. // Configure the HTTP request pipeline.
  14. if (app.Environment.IsDevelopment())
  15. {
  16. app.UseSwagger();
  17. app.UseSwaggerUI();
  18. }
  19. app.UseAuthorization();
  20. app.MapControllers();
  21. app.Run();

在创建的测试领域服务中添加测试的方法,就可以使用了

这里通过依赖注入获取到SqlSugar的对象就可以使用SqlSugar进行数据库查询了

  1. using SqlSugar;
  2. using TeacherCertification.Domain.TestGrowing;
  3. namespace TeacherCertification.Application.TestGrowing
  4. {
  5. public class TasksAppService : ITasksAppService
  6. {
  7. // 通过依赖注入获取到SqlSugar的对象
  8. private readonly ISqlSugarClient _db;
  9. public TasksAppService(ISqlSugarClient db)
  10. {
  11. _db = db;
  12. }
  13. public void GetTasks()
  14. {
  15. List<Tasks> tasks = _db.Queryable<Tasks>().Take(10).ToList();
  16. }
  17. }
  18. }

当然现在这一层直接使用到了SqlSugar的对象,所以也要添加一下引用:

  1. <ItemGroup>
  2. <PackageReference Include="SqlSugarCore" Version="5.1.4.169" />
  3. </ItemGroup>

截止到目前的示例代码下载

链接: https://pan.baidu.com/s/1WQQ1YGaccF8WJ29jyuOP1g?pwd=qc4g 提取码: qc4g 复制这段内容后打开百度网盘手机App,操作更方便哦

六:改造SqlSugar的使用,使用仓储

这个是sqlsugar官方关于仓储的文档:https://www.donet5.com/Home/Doc?typeId=1228

先按照官方文档那样直接使用sqlsugar的仓储非常简单

在领域层创建一个Repository类,代码结构如下:

在Repositor类中加入如下代码:

  1. using SqlSugar;
  2. namespace TeacherCertification.Domain
  3. {
  4. public class Repository<T> : SimpleClient<T> where T : class, new()
  5. {
  6. public Repository(ISqlSugarClient db)
  7. {
  8. base.Context = db;
  9. }
  10. /// <summary>
  11. /// 扩展方法,自带方法不能满足的时候可以添加新方法
  12. /// </summary>
  13. /// <returns></returns>
  14. public List<T> CommQuery(string json)
  15. {
  16. return null;
  17. }
  18. }
  19. }

然后在主项目的Program.cs去加入仓储类的注入:

  1. builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
  2. // 注入SqlSugar的仓储
  3. builder.Services.AddScoped(typeof(Repository<>));
  4. builder.Services.AddSqlsugar();
  5. builder.Services.AddTransient<ITasksAppService, TasksAppService>();

然后就可以在应用层去使用了:

  1. using TeacherCertification.Domain;
  2. using TeacherCertification.Domain.TestGrowing;
  3. namespace TeacherCertification.Application.TestGrowing
  4. {
  5. public class TasksAppService : ITasksAppService
  6. {
  7. // 通过依赖注入获取到SqlSugar仓储的实例
  8. private readonly Repository<Tasks> _taskRepository;
  9. public TasksAppService(Repository<Tasks> taskRepository)
  10. {
  11. _taskRepository = taskRepository;
  12. }
  13. public void GetTasks()
  14. {
  15. List<Tasks> tasks = _taskRepository.GetList();
  16. /*
  17. * .Context可以拿到db对象,但是按照领域驱动的思想这里不应该直接去依赖与具体的持久化层了,
  18. * 而是依赖于仓储这种更抽象的东西,当然很多公司每天加班业务都做不完,为了方便也是很多直接在应用层就依赖持久化层了
  19. */
  20. List<Tasks> tasks2 = _taskRepository.Context.Queryable<Tasks>().Take(10).ToList();
  21. }
  22. }
  23. }
直接使用sqlsugar仓储示例代码下载

链接: https://pan.baidu.com/s/1SK_LlWg4-CGB_OHCXfpw6w?pwd=c6e8 提取码: c6e8 复制这段内容后打开百度网盘手机App,操作更方便哦

改造一下sqlsugar仓储的使用,让仓储依赖于接口,而不是具体的实现

sqlsugar官网仓储的使用是直接注入的一个实现类,这样领域层就直接依赖于持久层的实现了,其实更好的做法是依赖于更抽象的接口,而不是具体的实现。仓储的实现应该放到数据库层去,应用层应该是依赖于抽象,而不关心具体的持久化实现,对这一层来说持久化用什么技术细节实现都可以,

先把领域层的仓储实现改成接口

代码结构如下:

具体的代码如下,代码非常简单,还要依赖sqlsugar的仓储接口:

  1. using SqlSugar;
  2. namespace TeacherCertification.Domain
  3. {
  4. /// <summary>
  5. /// 仓储的实现其实应该放到数据库层去,这一层应该是依赖于抽象,
  6. /// 而不依赖于具体的实现。但是这里是用的SqlSugar作为基础设施层的数据库层,
  7. /// 所以需要依赖于SqlSugar封装的一些接口。
  8. /// 正确的依赖关系应该是基础设置层依赖于领域层。
  9. /// 我们分层最主要的作用就是避免领域层遭受技术语言的入侵,像数据库这种持久化的具体细节应该是
  10. /// 基础设置层的事情
  11. /// </summary>
  12. public interface IRepository<T> : ISugarRepository, ISimpleClient<T> where T : class, new()
  13. {
  14. }
  15. }
然后仓储的具体实现就要放到基础设置层中的持久化层去了

代码如下:

  1. using SqlSugar;
  2. using TeacherCertification.Domain;
  3. namespace TeacherCertification.ORMCore
  4. {
  5. public class Repository<T> : SimpleClient<T>, IRepository<T> where T : class, new()
  6. {
  7. public Repository(ISqlSugarClient db)
  8. {
  9. base.Context = db;
  10. }
  11. }
  12. }
然后在主项目的Program.cs注入一下仓储接口与仓储实现就可以使用了

在主项目的Program.cs注入一下仓储接口与仓储实现:

  1. builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
  2. // 注入SqlSugar的仓储
  3. builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
  4. builder.Services.AddSqlsugar();
  5. builder.Services.AddTransient<ITasksAppService, TasksAppService>();

然后就可以使用这个仓储接口了:

  1. using TeacherCertification.Domain;
  2. using TeacherCertification.Domain.TestGrowing;
  3. namespace TeacherCertification.Application.TestGrowing
  4. {
  5. public class TasksAppService : ITasksAppService
  6. {
  7. // 通过依赖注入获取到SqlSugar仓储的实例
  8. private readonly IRepository<Tasks> _taskRepository;
  9. public TasksAppService(IRepository<Tasks> taskRepository)
  10. {
  11. _taskRepository = taskRepository;
  12. }
  13. public void GetTasks()
  14. {
  15. List<Tasks> tasks = _taskRepository.GetList();
  16. /*
  17. * .Context可以拿到db对象,但是按照领域驱动的思想这里不应该直接去依赖与具体的持久化层了,
  18. * 而是依赖于仓储这种更抽象的东西,当然很多公司每天加班业务都做不完,为了方便也是很多直接在应用层就依赖持久化层了
  19. */
  20. List<Tasks> tasks2 = _taskRepository.Context.Queryable<Tasks>().Take(10).ToList();
  21. }
  22. }
  23. }
改造仓储后示例代码下载

链接: https://pan.baidu.com/s/1WVUWR8rEpTL5ptuJQph1Ww?pwd=bwhq 提取码: bwhq 复制这段内容后打开百度网盘手机App,操作更方便哦

当封装的仓储不满足使用的时候其实应该单独在提供一个操作的接口出来

  1. namespace TeacherCertification.Domain.TestGrowing
  2. {
  3. /// <summary>
  4. /// 封装的基础仓储不满足的时候其实应该走单独在提供一个
  5. /// 操作的接口出来的,这样可以让应用层不直接依赖于持久化层的
  6. /// 实现细节,都是依赖于抽象的,具体的细节应该是在基础设置层的持久化实现里边,
  7. /// 只是我们平时为了方便直接就在应用层拿到持久化实现的对象去直接操作数据库了。
  8. /// </summary>
  9. public interface ITasksRepository
  10. {
  11. }
  12. }

七:改造手动依赖注入,使用Autofac实现自动注入

在主项目中加入Autofac的引用

  1. <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="9.0.0" />

Autofac.Extensions.DependencyInjection已经包含了Autofac基础库的引用

然后在需要使用注意注入的层里边添加一个获取程序集名称的类与方法(非必须可以省略这个,直接写死)

  1. using System.Reflection;
  2. namespace TeacherCertification.Application
  3. {
  4. public static class ApplicationServiceAutofac
  5. {
  6. /// <summary>
  7. /// 获取程序集名称
  8. /// </summary>
  9. /// <returns></returns>
  10. public static string? GetAssemblyName()
  11. {
  12. return Assembly.GetExecutingAssembly().GetName().Name;
  13. }
  14. }
  15. }

创建一个自动注入的逻辑类AutofacModuleRegister

加入自动注入的逻辑:

  1. using Autofac;
  2. using System.Reflection;
  3. using TeacherCertification.Application;
  4. namespace TeacherCertificationAPI.IOC
  5. {
  6. /// <summary>
  7. /// Autofac自动注入实现
  8. /// </summary>
  9. public class AutofacModuleRegister : Autofac.Module
  10. {
  11. /// <summary>
  12. ///
  13. /// </summary>
  14. /// <param name="builder"></param>
  15. protected override void Load(ContainerBuilder builder)
  16. {
  17. //自动注入Application层的(也可以直接写死名称就不用在需要注入的层去写获取名称的方法了)
  18. string? appAssemblyNam = ApplicationServiceAutofac.GetAssemblyName();
  19. if (appAssemblyNam != null)
  20. {
  21. var assemblysServices = Assembly.Load(appAssemblyNam);
  22. builder.RegisterAssemblyTypes(assemblysServices)
  23. .Where(x => x.Name.EndsWith("AppService", StringComparison.OrdinalIgnoreCase))
  24. .AsSelf()//注入自身
  25. .AsImplementedInterfaces()
  26. .InstancePerLifetimeScope();
  27. //.PropertiesAutowired(); // 开启属性注入;
  28. }
  29. }
  30. }
  31. }

然后在Program.cs中去写好相关注入即可

  1. builder.Host
  2. .UseServiceProviderFactory(new AutofacServiceProviderFactory())
  3. .ConfigureContainer<ContainerBuilder>(builder =>
  4. {
  5. builder.RegisterModule(new AutofacModuleRegister());
  6. });

好了,完成上述的步骤,应用层的接口和实现就不需要自动注入了,其他层的注入原理一样。但是要注意位置不要写到builder.Build()下面去了

.net使用Autofac实现自动注入示例代码下载

链接: https://pan.baidu.com/s/1cyh_zgRHxT8_pNQEJslZtw?pwd=xqyw 提取码: xqyw 复制这段内容后打开百度网盘手机App,操作更方便哦

八:AutoMapper的使用

一:添加AutoMapper依赖

  1. <PackageReference Include="AutoMapper" Version="13.0.1" />

或者使用命令安装:

  1. Install-Package AutoMapper

在需要的层都添加一下这个依赖,比如我这里在主项目和应用层都添加了一下

二:创建 AutoMapper 配置

你需要创建一个配置类来定义源对象和目标对象之间的映射关系,放到应用层里边去

  1. using AutoMapper;
  2. using TeacherCertification.Application.TestGrowing.Dto;
  3. using TeacherCertification.Domain.TestGrowing;
  4. namespace TeacherCertification.Application
  5. {
  6. public class ApplicationAutoMapperProfile : Profile
  7. {
  8. public ApplicationAutoMapperProfile()
  9. {
  10. CreateMap<TaskDto, Tasks>().ReverseMap();
  11. CreateMap<UpdateTaskDto, Tasks>().ReverseMap();
  12. CreateMap<MenuDto, Menu>().ReverseMap();
  13. CreateMap<UpdateMenuDto, Menu>().ReverseMap();
  14. CreateMap<MenuMetaDto, Menu>().ReverseMap();
  15. // 单向映射
  16. CreateMap<Menu,MenuMetaDto>();
  17. // 指定了字段映射的
  18. //CreateMap<Menu, MenuMetaDto>()
  19. // .ForMember(dest => dest.IsHide, opt => opt.MapFrom(src => src.IsHide))
  20. // .ForMember(dest => dest.IsAffix, opt => opt.MapFrom(src => src.IsAffix));
  21. }
  22. }
  23. }

三:注册 AutoMapper

在 Program.cs 文件中需要注册AutoMapper

  1. // 注册 AutoMapper
  2. builder.Services.AddAutoMapper(typeof(ApplicationAutoMapperProfile));

四:使用 AutoMapper

经过上面的步骤后就可以在各层使用AutoMapper了

  1. using AutoMapper;
  2. using TeacherCertification.Application.TestGrowing.Dto;
  3. using TeacherCertification.Domain;
  4. using TeacherCertification.Domain.TestGrowing;
  5. namespace TeacherCertification.Application.TestGrowing
  6. {
  7. public class TasksAppService : ITasksAppService
  8. {
  9. // 通过依赖注入获取到SqlSugar仓储的实例
  10. private readonly IRepository<Tasks> _taskRepository;
  11. private readonly IMapper _mapper;
  12. public TasksAppService(IRepository<Tasks> taskRepository, IMapper mapper)
  13. {
  14. _taskRepository = taskRepository;
  15. _mapper = mapper;
  16. }
  17. public List<TaskDto> GetTasks()
  18. {
  19. List<Tasks> tasks = _taskRepository.GetList();
  20. List<TaskDto> result = _mapper.Map<List<Tasks>, List<TaskDto>>(tasks);
  21. return result;
  22. }
  23. }
  24. }

五:使用 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的实例

  1. public abstract class ApplicationService
  2. {
  3. public IServiceProvider? ServiceProvider { get; set; }
  4. protected IMapper ObjectMapper
  5. {
  6. get
  7. {
  8. IMapper? mapper = ServiceProvider?.GetService<IMapper>();
  9. return mapper;
  10. }
  11. }
  12. }

然后应用服务继承ApplicationService即可直接使用AutoMapper了

  1. public class TasksAppService : ApplicationService,ITasksAppService
  2. {
  3. // 通过依赖注入获取到SqlSugar仓储的实例
  4. private readonly IRepository<Tasks> _taskRepository;
  5. public TasksAppService(IRepository<Tasks> taskRepository)
  6. {
  7. _taskRepository = taskRepository;
  8. }
  9. public List<TaskDto> GetTasks()
  10. {
  11. List<Tasks> tasks = _taskRepository.GetList();
  12. List<TaskDto> result = ObjectMapper.Map<List<Tasks>, List<TaskDto>>(tasks);
  13. return result;
  14. }
  15. }

十:通过特性封装事务,特性实现工作单元,通用统一的事务封装

添加一个名称叫TransactionFilter的过滤器:

加入如下代码:

  1. /// <summary>
  2. /// Sqlsugar全局事务,IActionFilter
  3. /// </summary>
  4. public class TransactionFilter : IActionFilter
  5. {
  6. ISqlSugarClient _db;//你也可以换EF CORE对象 或者ADO对象都行
  7. /// <summary>
  8. ///
  9. /// </summary>
  10. /// <param name="db"></param>
  11. public TransactionFilter(ISqlSugarClient db)//(ISqlSugarClient)需要IOC注入处理事务的对象
  12. {
  13. _db = db;
  14. }
  15. /// <summary>
  16. ///
  17. /// </summary>
  18. /// <param name="context"></param>
  19. public void OnActionExecuting(ActionExecutingContext context)
  20. {
  21. _db.AsTenant().BeginTran();//接口要加.AsTenant()
  22. }
  23. /// <summary>
  24. ///
  25. /// </summary>
  26. /// <param name="context"></param>
  27. public void OnActionExecuted(ActionExecutedContext context)
  28. {
  29. if (context.Exception == null)
  30. {
  31. _db.AsTenant().CommitTran();
  32. }
  33. else
  34. {
  35. _db.AsTenant().RollbackTran();
  36. //_db.AsTenant().RollBack();
  37. }
  38. }
  39. }

在Program.cs中去注入一下封装的过滤器

  1. //注入事务对象
  2. builder.Services.AddScoped<TransactionFilter>();

然后在接口加上过滤器的使用就可以了

  1. [ServiceFilter(typeof(TransactionFilter))]//加上这行就可以用了
  2. [HttpGet(Name = "GetWeatherForecast")]
  3. public IEnumerable<WeatherForecast> Get()
  4. {
  5. return null
  6. }

这样请求接口里边所有调用的方法,所有使用sqlsugar操作数据库的地方都会用到事务了(期间不要再去重新手动开启或者提交事务了,且上下文对象要保证是一个)

原理:
原理其实非常的简单,因为正常的操作在一次请求中sqlsugar的上下文对象都是同一个,所有这个请求这一次请求所有操作数据库都是在同一次事务中的,当然前提是期间不要再去重新手动开启或者提交事务了,且上下文对象要保证是一个(只要是正常的操作都会是同一个上下文对象的,因为都是从依赖注入中获取的,且依赖注入生命周期是这样设置的)


欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739。有需要软件开发,或者学习软件技术的朋友可以和我联系~(Q:815170684)

评价

忘掉过去式

2024/12/12 14:33:55

剑哥流批

DDD介绍

领域服务(Domain Services)领域中的一些概念不太适合建模为对象,即归类到实体对象或值对象,因为它们本质上就是一些操作,...

领域驱动设计DDD的一点理解

有人误认为项目架构中加入xxRepository,xxDomain,xxValueObject就变成了DDD架构。如果没有悟出其精髓就在项目中加入这些...

深入理解DDD的聚合模式

[TOC]DDD中的聚合模式是最难弄清楚的一种模式,在如何确定聚合边界这个问题上,更没有一个明确的指导原则,这导致DDD的落地...

领域驱动设计DDD ABP VNext 一:项目架构搭建模块使用

[TOC]用户接口层改造nuget中下载abp依赖VoLo.Abp.AspNetCore.Mvc ItemGroup添加方式 &lt;ItemGroup&gt; &lt;Packa...

领域驱动设计DDD之Repository

DDD中的Repository模式Repository模式也称存储库模式或仓储模式,根据Eric Evans的《领域驱动设计》一书,“存储库是一种封...

领域驱动设计DDD ABP VNext 二:使用仓储

[TOC]领域驱动设计仓储介绍在领域层和数据映射层之间进行中介,使用类似集合的接口来操作领域对象.” (Martin Fowler)。 实...

领域驱动设计DDD ABP VNext 三:领域模型 之 失血模型贫血模型充血模型胀血模型

[TOC]领域模型分为:失血模型,贫血模型,充血模型,胀血模型。 一、失血模型传统的三层架构,实体对象就是简单的POJO或者...

领域启动设计DDD 应用服务与领域服务区别

1.领域服务和Application Services 是不同的,Application Services 返回的是DTO,而领域服务返回的是领域对象(实体或者值...

领域启动设计DDD 领域服务与应用服务的区别

在这篇文章中,我们将看一下领域域服务与应用服务有什么不同。 人们常说,领域服务是承载那些不自然地适合实体和值对象的...

uin框架搭建

app.vue中创建.main_content{ position:absolute;top:0;right:0;bottom:0;left:0;overflow:hidden; display:flex;fle...

css弹性盒子flex布局

css弹性盒子由于版本不同浏览器问题造成了一些不同的写法display:flexbox;在google浏览器中如果使用下面的写法就不行displa...

可输入下拉文本框据输入动态加载数据 jquery-editable-select

用到一个jquery-editable-select的控件github地址:https://github.com/indrimuska/jquery-editable-select这个插件的原理是...

.net mvc分部页.net core分部页

.net分部页的三种方式第一种:@Html.Partial(&quot;_分部页&quot;)第二种:@{ Html.RenderPartial(&quot;分部页&quot;);}...

css中单位pxemrem和vh/vw的理解

&gt;px像素(Pixel)。相对长度单位。像素px是相对于显示器屏幕分辨率而言的。em是相对长度单位。相对于当前对象内文本的字...

让IIS支持webp格式图片让IIS支持vtt格式iis设置mime类型iis配置支持的类型

webp格式图片可以让图片体积变小。也让下载图片变得更加困难一点 在线制作webp工具 https://www.upyun.com/webp?utm_mediu...