news 2026/4/23 15:39:27

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/4/23 13:09:33

基于改进蛇优化算法(GOSO/ISO)优化BP神经网络的数据回归预测探索

基于改进蛇优化算法(GOSO/ISO)优化BP神经网络的数据回归预测(GOSO/ISO-BP) 蛇优化算法SO是2022年提出的新算法&#xff0c;性能优异&#xff0c;目前应用较少&#xff0c;改进蛇优化算法GOSO/ISO应用更少&#xff0c;适合PAPER 改进点1为在初始化种群引入混沌映射&#xff0c;本…

作者头像 李华
网站建设 2026/4/23 14:48:22

数据结构总结笔记

1 数据结构三要素是什么&#xff1f;逻辑结构包括什么&#xff1f;存储结构包括什么&#xff1f; 数据结构三要素&#xff1a;逻辑结构、存储结构、数据运算。 逻辑结构包括线性结构和非线性结构&#xff1a; 线性结构&#xff1a;线性表、栈、队列&#xff1b;非线性结构&a…

作者头像 李华
网站建设 2026/4/18 5:14:13

PHP 开发者必须掌握的基本 Linux 命令

开始 PHP 开发时&#xff0c;我并没有特别的理由选择 Linux。既不是出于对开源软件的热情&#xff0c;也不是认为 Linux 比 macOS 或 Windows 更优秀。当时只是希望找到一个更轻量、更可定制的开发环境。对我而言&#xff0c;Linux 就是一个可能提升编码效率的工具。随着项目规…

作者头像 李华
网站建设 2026/4/23 13:13:54

用格子玻尔兹曼方法(LBM)模拟不可压缩密度守恒压力驱动流

格子玻尔兹曼方法&#xff08;LBM&#xff09;模拟不可压缩密度守恒压力驱动流&#xff0c;MATLAB代码嘿&#xff0c;大家好&#xff01;今天来聊聊如何使用格子玻尔兹曼方法&#xff08;LBM&#xff09;模拟不可压缩密度守恒压力驱动流&#xff0c;并且会用MATLAB代码来实现。…

作者头像 李华
网站建设 2026/4/23 13:39:49

基于LQR控制算法的四轮独立电驱动汽车横向稳定性控制研究

适用方向&#xff1a;基于LQR控制算法的直接横摆力矩控制&#xff08;DYC&#xff09;的四轮独立电驱动汽车的横向稳定性控制研究 主要内容&#xff1a;利用carsim建模&#xff0c;在simulink中搭建控制器&#xff0c;然后进行联合。 实现汽车在高速低附着路面下完成双移线工况…

作者头像 李华
网站建设 2026/4/23 13:39:56

IPv6违规外联防御指南:从风险盲区到可管可控

在数字化转型加速推进的今天&#xff0c;IPv6作为下一代互联网协议&#xff0c;正以其庞大的地址空间和高效的通信机制&#xff0c;推动着网络基础设施的全面升级。根据APNIC监测数据&#xff0c;2022年全球IPv6支持能力持续增强&#xff0c;支持率从2021年3月的28.31%显著提升…

作者头像 李华