news 2026/5/1 9:55:35

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/16 19:11:25

市场快评 · 今日复盘要点20251217

Q1:今日是否适合低吸? A:今日上涨个股3626 家,大于2500家阈值, 市场情绪得到修复, 把仓位加到 60%。 Q2:昨日最高标今日表现如何? A:昨日最高标 百大集团 今日继续涨停。 3:今日主线题材及板块龙头是谁? A:主线为 智能驾驶,板块龙头 暂时看不出来,核心容量标的为…

作者头像 李华
网站建设 2026/4/30 15:04:26

强化学习围捕仿真系统优化方案

强化学习围捕仿真系统优化方案 一、问题分析与现状评估 1.1 当前问题分析 在强化学习围捕仿真任务中,主要存在以下核心问题: 速度劣势:围捕艇速度慢于目标,无法通过速度优势直接捕获 成功率极低:2万轮次仅成功20多次,成功率约0.1% 学习效率低下:算法未能有效学习到有…

作者头像 李华
网站建设 2026/5/1 9:14:37

筛选器管理模块 Cordova 与 OpenHarmony 混合开发实战

&#x1f4cc; 概述 筛选器管理模块允许用户保存和管理常用的筛选条件。该模块集成了 Cordova 框架与 OpenHarmony 原生能力&#xff0c;提供了完整的筛选器管理功能。用户可以创建多个筛选器&#xff0c;为每个筛选器设置特定的条件&#xff0c;然后快速应用这些筛选器来查看特…

作者头像 李华
网站建设 2026/5/1 1:58:54

每日统计模块 Cordova 与 OpenHarmony 混合开发实战

&#x1f4cc; 概述 每日统计模块提供了每日喝茶数据的统计分析功能。该模块集成了 Cordova 框架与 OpenHarmony 原生能力&#xff0c;实现了高效的数据统计和可视化展示。用户可以查看特定日期的喝茶记录总数、消费金额、平均评分等统计数据。模块支持日期范围选择和数据导出。…

作者头像 李华
网站建设 2026/4/25 4:02:09

AWS SageMaker SDK 完整教程:从零开始云端训练你的模型 _

一、SageMaker介绍Amazon SageMaker 是 AWS 提供的全托管机器学习平台&#xff0c;它覆盖了从数据准备、模型训练、超参数调优到模型部署的完整流程&#xff0c;我们可以通过 SageMaker&#xff0c;轻松创建 Notebook 实例进行数据探索和实验&#xff0c;也可以使用AWS的计算资…

作者头像 李华