Source Generators实现简版AutoMapper

问题

在业务开发中,我们常常需要将一个对象映射成另一个对象。例如将领域实体(UserEntity)映射成暴露给服务外部使用的数据传输对象(UserDto)。

AutoMapper则是目前主流的解决方案,实现类似如下代码:

var configuration = new MapperConfiguration(cfg => 
{cfg.CreateMap<UserEntity, UserDto>();
});var mapper = configuration.CreateMapper();var userEntity = GetFromDB();
var userDto = mapper.Map<UserDto>(userEntity);

相对于使用AutoMapper,我更倾向于显式映射,类似如下代码:

public UserDto MapToUserDto(UserEntity entity)
{return new UserDto {Id = entity.Id,Name = entity.Name};
}var userEntity = GetFromDB();
var userDto = MapToUserDto(userEntity);

显式映射有以下一些好处:

  • 不依赖第三方框架,性能有保障

  • 设计时支持,例如"查找所有引用"

  • 运行时支持,例如"断点调试"

但是缺点也很明显,手工编写显式映射是一项耗时并且枯燥的工作。

虽然可以使用工具(例如代码生成器)自动生成这些映射代码,但是今天我们介绍一种更方便的方式。

Source Generators

上次我们已经介绍过Source Generators,它可以在编译时创建并添加到编译中的代码,而无需像代码生成器那样显式生成大量冗余代码。

因此,我们这次尝试用Source Generators来自动生成显式映射代码。

实现代码如下:

[Generator]
public class AutoMapperGenerator : ISourceGenerator
{private const string MappingAttributeText = @"
using System;
namespace AutoMapperGenerator
{
public class AutoMappingAttribute : Attribute
{public AutoMappingAttribute(Type fromType,Type toType){this.FromType = fromType;this.ToType = toType;}public Type FromType { get; set; }public Type ToType { get; set; }
}
}";public void Initialize(GeneratorInitializationContext context){}public void Execute(GeneratorExecutionContext context){context.AddSource("AutoMappingAttribute", SourceText.From(MappingAttributeText, Encoding.UTF8));var options = (context.Compilation as CSharpCompilation).SyntaxTrees[0].Options as CSharpParseOptions;var compilation = context.Compilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(SourceText.From(MappingAttributeText, Encoding.UTF8), options));var allNodes = compilation.SyntaxTrees.SelectMany(s => s.GetRoot().DescendantNodes());var allAttributes = allNodes.Where((d) => d.IsKind(SyntaxKind.Attribute)).OfType<AttributeSyntax>();var attributes = allAttributes.Where(d => d.Name.ToString() == "AutoMapping").ToList();var allClasses = compilation.SyntaxTrees.SelectMany(x => x.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>());var sourceBuilder = new StringBuilder(@"
//<auto-generated>
namespace AutoMapperGenerator
{
public static class Mapper
{");foreach (AttributeSyntax attr in attributes){var fromTypeArgSyntax = attr.ArgumentList.Arguments.First();var fromTypeArgSyntaxExpr = fromTypeArgSyntax.Expression.NormalizeWhitespace().ToFullString();var toTypeArgSyntax = attr.ArgumentList.Arguments.ElementAt(1);var toTypeArgSyntaxExpr = toTypeArgSyntax.Expression.NormalizeWhitespace().ToFullString();var fromClassName = GetContentInParentheses(fromTypeArgSyntaxExpr);var fromClassSyntax = allClasses.First(x => x.Identifier.ToString() == fromClassName);var fromClassModel = compilation.GetSemanticModel(fromClassSyntax.SyntaxTree);var fromClassNamedTypeSymbol = ModelExtensions.GetDeclaredSymbol(fromClassModel, fromClassSyntax);var fromClassFullName = fromClassNamedTypeSymbol.OriginalDefinition.ToString();var toClassName = GetContentInParentheses(toTypeArgSyntaxExpr);var toClassSyntax = allClasses.First(x => x.Identifier.ToString() == toClassName);var toClassModel = compilation.GetSemanticModel(toClassSyntax.SyntaxTree);var toClassNamedTypeSymbol = ModelExtensions.GetDeclaredSymbol(toClassModel, toClassSyntax);var toClassFullName = toClassNamedTypeSymbol.OriginalDefinition.ToString();           sourceBuilder.Append($@"public static {toClassFullName} To{toClassName}(this {fromClassFullName} source){{var target = new {toClassFullName}();");var propertySyntaxes = toClassSyntax.SyntaxTree.GetRoot().DescendantNodes().OfType<PropertyDeclarationSyntax>();foreach (var propertySyntaxe in propertySyntaxes){var symbol = toClassModel.GetDeclaredSymbol(propertySyntaxe);var propertyName = symbol.Name;sourceBuilder.Append($@"target.{propertyName} = source.{propertyName};");}sourceBuilder.Append(@"return target;}
");               }sourceBuilder.Append(@"
}
}");context.AddSource("Mapper", SourceText.From(sourceBuilder.ToString(), Encoding.UTF8));}private string GetContentInParentheses(string value){var match = Regex.Match(value, @"\(([^)]*)\)");return match.Groups[1].Value;}
}

我们定义了AutoMappingAttribute,可以在任意类上声明此Attribute。

AutoMappingAttribute包含FromType和ToType参数,Source Generators为FromType生成ToXXX的扩展方法,遍历ToType对应类的所有属性并显示映射。

使用示例

示例代码如下:

[ApiController]
[Route("[controller]")]
[AutoMapping(typeof(UserEntity), typeof(UserDto))]
public class UserController : ControllerBase
{ [HttpGet]public UserDto Get(int id){var userEntity = GetFromDB(id);var userDto = userEntity.ToUserDto();return userDto;}
}

UserController上声明了AutoMappingAttribute,编译后可以看到,自动生成了ToUserDto方法:

运行后测试,工作正常,成功!

结论

当然,目前的功能与真正的AutoMapper还相差很远。

但是,如果你也希望在代码中使用显式映射,本文将是一个很好的起点。

如果你觉得这篇文章对你有所启发,请关注我的个人公众号”My IO“,记住我!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/300017.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

15岁大学毕业,一生800多篇专著论文,双眼失明却凭一条公式称霸数学界

全世界只有3.14 %的人关注了青少年数学之旅从文明之火初燃的那一刻起数学就与之为伴从万年前“数”的产生到现代科技的迅猛发展数学不仅是窥探宇宙万物的入口也是最高智慧的结晶在漫漫的人类历史长河中各个伟大的数学家犹如布满“数学星空”中的星群他们追求最高的精确最合理的…

Java程序员应该知道的10个Eclipse调试技巧

为什么80%的码农都做不了架构师&#xff1f;>>> Eclipse是众多Java程序员实用的开发工具&#xff0c;其中开发技巧也是繁多&#xff0c;但作为优秀的Java程序员&#xff0c;需要掌握最起码的调试技巧。 1 条件断点 2 异常断点 3 监视点 4 评估/检查 5 修改变量值 6…

.NET 平台采用率的提升归功于开源

微软近日发布了一篇博客&#xff0c;以对话形式与 .NET 工程师探讨了一下有关 .NET 项目的开源经验和心得。其中指出&#xff0c;.NET 软件开发平台采用率的提升主要归功于开源。根据介绍&#xff0c;微软在 2015 年将其 CoreCLR .NET 执行引擎作为开源发布&#xff0c;并在 20…

新世纪英汉词典 | 今日最佳

全世界只有3.14 %的人关注了青少年数学之旅&#xff08;图源 荷兰鸭&#xff0c;侵权删&#xff09;

发布一个biztalk 解析Excel文件到xml消息的管道组件

<?xml:namespace prefix o /><?xml:namespace prefix v /> 此pipeline component主要功能是通过ODBC的excel驱动把excel文件转成xml的消息。excel文件可以是biztalk通过从各种适配器获得&#xff0c;比如从file、ftp、msmq、http等等适配器获得excel文件的数…

点歌软件测试自学,实际歌唱对比测试

四、实际歌唱对比测试光说不练假把戏&#xff0c;卡拉OK软件真正PK还是在实唱方面。所以我们现在就来测试一下这四款软件在真人真唱方面表现的到底怎么样。四款软件在点唱歌曲时都需要在线下载&#xff0c;经过测试&#xff0c;在普通ADSL(1M带宽)的环境下歌曲加载速度都不慢&a…

不懂物理,何以谈科技?

全世界只有3.14 % 的人关注了青少年数学之旅孩子对这个世界有着最纯粹的好奇&#xff0c;经常会问许许多多他们在日常生活中遇到的种种问题。天空为什么是蓝色的&#xff1f;电是怎么来的&#xff1f;为什么不管怎么使劲儿蹦&#xff0c;还是会落到地上&#xff1f;像小木的表妹…

硬盘结构及硬盘错误的解决方法(一)

硬盘的结构一、物理结构&#xff1a;硬盘在物理结构上由头盘组件和控制电路板两大部分组成。 ㈠ 头盘组件头盘就是磁头和盘片的意思。头盘组件包括盘体、电机、磁头等部件。所有部件密封在外壳中&#xff0c;绝对无尘、真空&#xff0c;如果你一旦开启了这个密封外壳&#xff0…

七0二所与江南计算机研究所,江南大学:一所被低估的“211”大学,2个A+学科,丝毫不输985...

文/角角老师导语&#xff1a;江南大学&#xff0c;尽管是一所“211工程”大学&#xff0c;但&#xff0c;在外省学生的意识里&#xff0c;看名字就是“野鸡大学”或者“民办大学”。其实&#xff0c;江南大学&#xff0c;实力很强&#xff0c;而是很低调&#xff0c;在教育部第…

Facebook 如何管理150亿张照片

Facebook 的照片分享很受欢迎&#xff0c;迄今&#xff0c;Facebook 用户已经上传了150亿张照片&#xff0c;加上缩略图&#xff0c;总容量超过1.5PB&#xff0c;而每周新增的照片为2亿2000万张&#xff0c;约25TB&#xff0c;高峰期&#xff0c;Facebook 每秒处理55万张照片&a…

Kubernetes全栈架构师(资源调度上)--学习笔记

▲ 点击上方“DotNet NB”关注公众号回复“1”获取开发者路线图学习分享 丨作者 / 郑 子 铭 这是DotNet NB 公众号的第171篇原创文章目录Replication Controller和ReplicaSet无状态服务Deployment概念Deployment的创建Deployment的更新Deployment的回滚Deployment扩容和缩容…

求护士的心理阴影面积 | 今日最佳

全世界只有3.14 %的人关注了青少年数学之旅&#xff08;图源 叛逆行为艺术&#xff0c;侵权删&#xff09;

RBAC 权限入门

RBAC 权限入门 RBAC概念 RBAC&#xff1a;Role Based Access Control&#xff0c;核心是用户只和角色关联&#xff0c;而某角色可以拥有各种各样的权限并可继承。 RBAC白话介绍 RBAC有很多模型&#xff0c;最简单的就是Core RBAC。说白了就是User用户, Role角色, Permission许可…

华为服务器如何用pe重装系统,教你华为u盘重装win10系统详细图文

华为笔记本电脑的外型真的很时尚&#xff0c;以及其轻薄便捷的特色&#xff0c;迅速吸引力一大波粉丝。目前的华为笔记本电脑使用的操作系统基本是win10系统了&#xff0c;win10系统升级经常出现错误&#xff0c;下面小编就给你讲解下U盘重装win10的方法。喜欢完游戏的朋友来说…

设计模式之建造者

建造者(生成器)模式含义&#xff1a;生成器模式是一种创建型模式&#xff0c;使你能够分步奏创建复杂对象&#xff0c;可使用相同的创建代码生成不同类型和形式的对象。看图我们就能很好地理解&#xff0c;图中就是工厂中的流水线模式&#xff0c;建造者就好比整条流水线&#…

索尼服务器维护时间,索尼云服务器

索尼云服务器 内容精选换一换如果密码丢失、或创建时未设置密码&#xff0c;推荐您在控制台设置登录密码。更新后端云服务器&#xff0c;可修改字段为后端云服务器的名称和权重&#xff0c;可以为性能好的服务器设置更大的权重&#xff0c;用来接收更多的流量。如果后端云服务器…

刚刚,陶哲轩惨遭3个物理学家狠狠打脸,一条数学公式或将引起教科书改革

这波操作把数学界都炸懵了就在刚刚&#xff0c; 3 位物理学家联合数学天才、菲尔兹奖得主陶哲轩&#xff0c;彻底炸翻了数学界。左往右&#xff0c;彼得丹顿、斯蒂芬帕克、张西宁彼得丹顿&#xff08;Peter B.Denton&#xff09;&#xff0c;美国布鲁克黑文国家实验室的助理物理…

教材管理系统紧张开发中

教材管理系统紧张开发中&#xff0c;系统也许不大&#xff0c;可是业务比较复杂、交叉性很强&#xff0c;教学计划、课程代码、教材、供货商、分校点、系部、教师、学生、库存、盘点、结算、数据采集、征订、订购、分发、收费一个都不能少&#xff01;既有C/S三层又有Web模式&a…

Blazor 组件之间使用 EventCallback 进行通信

翻译自 Waqas Anwar 2021年3月28日的文章 《Communication between Blazor Components using EventCallback》 [1]Blazor 应用程序是相互交互的多个 Blazor 组件的集合&#xff0c;我们可以在其他父组件中使用子组件。在实际的应用程序中&#xff0c;将数据或事件信息从一个组件…

***教程十:数据库注入(上)

这一段的教程是笔者&#xff08;这里应该叫整理者&#xff09;由《***X档案》的教程中整理改编而来。不知道是什么时候&#xff0c;B/S结构的软件越来越普及。工程师们为了应对“用户皆白痴”的服务理念&#xff08;就是把用户想像成什么都不会&#xff09;&#xff0c;所以更加…