当前位置: 首页 > news >正文

PocoEmit遥遥领先于AutoMapper之打通充血模型的任督二脉

一、充血模型和失血模型

1. 充血模型的优势

充血模型更加OOP

充血模型代码可读性更好

1.1 充血模型伪代码

var messageDto = controller.ReadDto();

var message = messageDto.ToEntity();

message.Save();

1.2 失血模型伪代码

var messageDto = controller.ReadDto();

var message = messageDto.ToEntity();

var messageService = controller.GetMessageService();

messageService.Save(message);

2. 充血模型爱你不容易

大部分程序员都知道充血模型好,想实现却很难

大部分业务逻辑都需要依赖外部服务

充血模型需要用到外部服务,又不想依赖外部服务的具体实现

很容易想到使用IOC的依赖注入来解决

我们给DTO注入服务还行,因为IOC参与了controller过程

当DTO发生转化时,新增的服务IOC还是有点力不从心

没法引用外部服务的充血模型气血不通,业务表达能力大大下降

这也是大部分人弃用充血模型的主要原因,不好用还不如不用

这个任督二脉PocoEmit可以帮你打通

二、首先来个Case演示一下

Dto转化为实体

但是实体有更多逻辑依赖外部服务,这些外部服务Dto不见得提供的了

这就需要注入

PocoEmit支持构造函数参数注入和属性注入

IMapper对象是默认支持注入的服务

1. Entity比Dto多出来的Mapper可以注入

class MessageDto

{

public string Message { get; set; }

}

class MessageEntity

{

public IMapper Mapper { get; set; }

public string Message { get; set; }

}

2. 转化并注入的代码

var mapper = Mapper.Create();

var dto = new MessageDto { Message = "Hello UseMapper" };

MessageEntity message = mapper.Convert<MessageDto, MessageEntity>(dto);

Assert.NotNull(message.Mapper);

三、再演示注入自定义的服务

1. UserDomain比Dto多出来的Repository可以注入

class UserDTO

{

public int Id { get; set; }

public string Name { get; set; }

}

class UserDomain(UserRepository repository, int id, string name)

{

private readonly UserRepository _repository = repository;

public UserRepository Repository

=> _repository;

public int Id { get; } = id;

public string Name { get; } = name;

// ...

}

class UserRepository

{

void Add(UserDomain user) { }

void Update(UserDomain entity) { }

void Remove(UserDomain entity) { }

public static readonly UserRepository Instance = new();

}

2. 注册、转化并注入的代码

通过UseDefault可以注入服务

IMapper mapper = Mapper.Create()

.UseDefault(UserRepository.Instance);

var dto = new UserDTO { Id = 1, Name = "Jxj" };

UserDomain user = mapper.Convert<UserDTO, UserDomain>(dto);

Assert.NotNull(user.Repository);

四、注入IOC容器的Case

注入IOC容器需要安装nuget包PocoEmit.ServiceProvider

1. 包含IOC容器的实体

class UserWithServiceProvider

{

public int Id { get; set; }

public string Name { get; set; }

public IServiceProvider ServiceProvider { get; set; }

}

2. 注册、转化并注入的代码

UseSingleton是把容器作为唯一容器注入

UseScope是使用当前Scope子容器

UseContext是在Mvc下,使用当前HttpContext的RequestServices子容器

var services = new ServiceCollection();

var serviceProvider = services.BuildServiceProvider();

var mapper = Mapper.Create();

mapper.UseSingleton(serviceProvider);

var dto = new UserDTO { Id = 1, Name = "Jxj" };

UserWithServiceProvider user = mapper.Convert<UserDTO, UserWithServiceProvider>(dto);

Assert.NotNull(user.ServiceProvider);

五、当然还可以注入容器内的服务

1. UserDomain多出来的Repository需要注入

这次我们用IOC来管理Repository

这样才能更好的利用依赖注入

Repository可能还会依赖其他的服务

手动维护服务对象可能会很麻烦,IOC容器擅长维护这些复杂关系

class UserDomain(UserRepository repository, int id, string name)

{

private readonly UserRepository _repository = repository;

public UserRepository Repository

=> _repository;

public int Id { get; } = id;

public string Name { get; } = name;

// ...

}

class UserRepository

{

void Add(UserDomain user) { }

void Update(UserDomain entity) { }

void Remove(UserDomain entity) { }

}

2. 注册、转化并注入的代码

通过UseScope注入IOC容器

通过UseDefault告知这个类型从IOC容器中注入

var services = new ServiceCollection()

.AddScoped<UserRepository>();

var serviceProvider = services.BuildServiceProvider();

var mapper = Mapper.Create();

mapper.UseScope(serviceProvider)

.UseDefault<UserRepository>();

var dto = new UserDTO { Id = 1, Name = "Jxj" };

UserDomain user = mapper.Convert<UserDTO, UserDomain>(dto);

Assert.NotNull(user.Repository);

六、支持IOC容器的特性

支持FromKeyedServices

支持FromServices

1. FromKeyedServices标记注入点和服务键

class UserDomain1([FromKeyedServices("User1")]UserRepository repository, int id, string name)

: UserDomain(repository, id, name)

{

}

class UserDomain2([FromKeyedServices("User2")] UserRepository repository, int id, string name)

: UserDomain(repository, id, name)

{

}

class UserDomain(UserRepository repository, int id, string name)

{

private readonly UserRepository _repository = repository;

public UserRepository Repository

=> _repository;

public int Id { get; } = id;

public string Name { get; } = name;

// ...

}

class UserRepository(string tableName)

{

private readonly string _tableName = tableName;

public string TableName

=> _tableName;

void Add(UserDomain user) { }

void Update(UserDomain entity) { }

void Remove(UserDomain entity) { }

}

2. 注册、转化并注入的代码

由于识别出FromKeyedServices,就不需要UseDefault

这样简洁又优雅

string table1 = "User1";

string table2 = "User2";

var services = new ServiceCollection()

.AddKeyedScoped(table1, (_, _) => new UserRepository(table1))

.AddKeyedScoped(table2, (_, _) => new UserRepository(table2));

var serviceProvider = services.BuildServiceProvider();

var mapper = Mapper.Create();

mapper.UseScope(serviceProvider);

var dto = new UserDTO { Id = 1, Name = "Jxj" };

UserDomain user = mapper.Convert<UserDTO, UserDomain1>(dto);

Assert.NotNull(user.Repository);

UserDomain user2 = mapper.Convert<UserDTO, UserDomain2>(dto);

Assert.NotNull(user2.Repository);

七、竞品类似的功能

1. AutoMapper不支持

AutoMapper的NullSubstitute用来指定源属性为null时的默认值

用AutoMapper实现类似功能需要复杂的自定义IValueResolver来实现

PocoEmit在源无法匹配或源字段为null都可能触发依赖注入

2. EF有类似功能

不过貌似只支持EF内部某些服务

请参阅 EF Core实体类的依赖注入

八、总结

1. OOM映射需要依赖注入

DTO、实体、领域模型如果有业务逻辑就需要依赖外部服务

支持按类型注入,也支持按指定的参数或属性注入

支持FromKeyedServices和FromServices

需要外部服务就需要依赖注入

2. PocoEmit的依赖注入助力程序分层架构

依赖注入的加持每一层想调用啥就调用啥

同时也让每一层更好的划分让调用啥,不让调用啥更容易控制

同时也让业务需要划分多少层就划分多层变得简单

3. IOC容器使用需要注意

简单作业单容器,使用UseSingleton即可

多线程需要使用UseScope

Mvc(含WebApi)逻辑处理使用UseContext

UseContext需要引用nuget包PocoEmit.Mvc

如果是Mvc异步处理或Quartz类似作业不要用UseContext

就怕异步中获取到了HttpContext,但执行中途被释放了,后面就可能异常了

http://www.cnnetsun.cn/news/115441.html

相关文章:

  • JavaEE进阶——SpringBoot日志从入门到精通
  • 结构体简单题
  • 时间序列回归预测:LSTM、CNN - LSTM、PSO - CNN - LSTM、GAPSO - CNN - LSTM大比拼
  • 飞轮储能系统的建模与 MATLAB 仿真:永磁同步电机作为飞轮驱动电机
  • 车间进度总卡壳?生产小工单的3个必备功能,90%企业都用错了
  • 如何用 ShedLock 让 Spring Boot 的定时任务在多实例环境下只执行一次
  • 基于MPC的永磁同步电机非线性终端滑模控制仿真研究
  • ISSA - CNN - BiLSTM多输入单输出回归的Python实现与改进
  • Q学习(Q-learning)路径规划算法实战
  • ANSYS/LS - dyna防爆涂层砂浆砖框架结构爆破荷载损伤响应案例探索
  • 基于TOA/FOA的无源定位方法MATLAB仿真探索
  • 基于一致性算法改进的自适应虚拟阻抗控制:解决双机并联功率分布不均
  • springboot框架对接物联网,配置TCP协议依赖,与设备通信,让TCP变的如此简单
  • 微软和布朗大学最新发现:让AI助手拥有18000多种技能的革命性突破
  • MATLAB仿真:二维TOA传感器网络定位与时钟偏差拟合,最小二乘求解
  • 【参数辨识】基于卡尔曼滤波(KF)估计离散线性系统对垂直起降(VTOL)飞行器的鲁棒辨识附matlab代码
  • 桥梁与隧道安全守护者 抗冰冻型风速监测方案
  • 05-FreeRTOS的内存管理
  • 基于改进蛇优化算法(GOSO/ISO)优化随机森林数据回归预测模型(含初始化种群混沌映射、减法...
  • 基于大数据的人脸识别系统设计与实现开题报告
  • 车载 Android 系统稳定性问题全解析:从性能到黑屏的排查指南
  • 气象在线监测系统助力智慧环境管理,金叶仪器专业气象监测解决方案
  • 【TVM 教程】交叉编译与 RPC
  • 腾讯云国际站代理商的QAPM服务能提供哪些专属服务?
  • 网安副业怎么选?漏洞挖掘、技术博客、竞赛奖金实战,哪个更适配你?
  • 量子计算验证方法:软件测试从业者的转型指南
  • 突破 Oracle/MySQL 瓶颈:金仓数据库以三重革新,筑牢业务转型 “数据底座”
  • 【学习神器】NotebookLM“播客”功能实战指南:四六级、考研党高效复习秘籍
  • 如何解决 pip install 网络报错 ERROR: No matching distribution found for requests
  • 12 Ways to Find User Account Info and Login Details in Linux