wordpress插件证书认证网站,golang 做网站,专做母婴食品的网站,给网站做翻译问题在业务开发中#xff0c;我们常常需要将一个对象映射成另一个对象。例如将领域实体(UserEntity)映射成暴露给服务外部使用的数据传输对象(UserDto)。而AutoMapper则是目前主流的解决方案#xff0c;实现类似如下代码#xff1a;var configuration new MapperConfigurat… 问题在业务开发中我们常常需要将一个对象映射成另一个对象。例如将领域实体(UserEntity)映射成暴露给服务外部使用的数据传输对象(UserDto)。而AutoMapper则是目前主流的解决方案实现类似如下代码var configuration new MapperConfiguration(cfg
{cfg.CreateMapUserEntity, UserDto();
});var mapper configuration.CreateMapper();var userEntity GetFromDB();
var userDto mapper.MapUserDto(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)).OfTypeAttributeSyntax();var attributes allAttributes.Where(d d.Name.ToString() AutoMapping).ToList();var allClasses compilation.SyntaxTrees.SelectMany(x x.GetRoot().DescendantNodes().OfTypeClassDeclarationSyntax());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().OfTypePropertyDeclarationSyntax();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“记住我