news 2026/7/10 12:35:24

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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,但执行中途被释放了,后面就可能异常了

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/10 11:41:04

让YOLO飞起来:从CPU到GPU的配置指南

最近在配置YOLO&#xff08;You Only Look Once&#xff09;进行物体检测和图像分割任务时&#xff0c;发现默认安装的情况下&#xff0c;YOLO使用的是CPU进行计算。这对于需要处理大量图像或实时检测的任务来说&#xff0c;效率明显不足。本文将详细介绍如何将YOLO从CPU模式切…

作者头像 李华
网站建设 2026/7/10 0:31:23

磁链观测器:从仿真到闭环代码实现

磁链观测器(仿真&#xff0b;闭环代码参考文档&#xff09; 1.仿真采用simulink搭建&#xff0c;2018b版本 2.代码采用Keil软件编译&#xff0c;思路参考vesc中使用的方法&#xff0c;自己编写的代码能够实现0速闭环启动&#xff0c;并且标注有大量注释&#xff0c;方便学习。 …

作者头像 李华
网站建设 2026/7/10 6:47:15

单机版RS485集中抄表软件:探索电表数据采集的奥秘

单机版RS485集中抄表软件&#xff0c;集中抄读645-2007协议的智能电表&#xff0c;645-1997的没有测试过&#xff0c;不清楚能不能抄&#xff0c;本地485有线集中抄表&#xff0c;配合485转网络可实现远程抄表在电力数据采集领域&#xff0c;单机版RS485集中抄表软件发挥着至关…

作者头像 李华
网站建设 2026/7/10 11:33:01

探索EKF算法在机器人轨迹定位中的神奇魅力

EKF算法做机器人轨迹定位/跟踪的程序&#xff0c;与里程计算法进行对比&#xff0c;结果显示EKF算法定位/跟踪精度更高。 纯里程计的误差为 error_Odom_average 1.0283 Ekf定位的误差为 error_Ekf_average 0.071629在机器人领域&#xff0c;轨迹定位和跟踪可是至关重要的任务…

作者头像 李华
网站建设 2026/7/10 11:35:07

echarts4升级为echarts5的常见问题

[ECharts] DEPRECATED: textStyle hierarchy in label has been removed since 4.0. All textStyle properties are configured in label directly now.[ECharts]已弃用&#xff1a;标签中的textStyle层次结构自4.0以来已被删除。现在&#xff0c;所有textStyle属性都直接在标签…

作者头像 李华
网站建设 2026/7/9 14:34:36

六西格玛证书等级真相超反转,绿带其实是企业的最爱!- 优思学院

为什么企业最爱绿带&#xff0c;而不是黑带&#xff1f;在国内制造业、互联网企业以及服务型组织中&#xff0c;六西格玛证书早已不是一个陌生名词。绿带、黑带、黑带大师这三个等级&#xff0c;看起来像是一条清晰的能力进阶路径&#xff1a;从工具型人才&#xff0c;到项目型…

作者头像 李华