日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Source Generators实现简版AutoMapper

發(fā)布時(shí)間:2023/12/4 编程问答 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Source Generators实现简版AutoMapper 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

問題

在業(yè)務(wù)開發(fā)中,我們常常需要將一個(gè)對象映射成另一個(gè)對象。例如將領(lǐng)域?qū)嶓w(UserEntity)映射成暴露給服務(wù)外部使用的數(shù)據(jù)傳輸對象(UserDto)。

而AutoMapper則是目前主流的解決方案,實(shí)現(xiàn)類似如下代碼:

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);

顯式映射有以下一些好處:

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

  • 設(shè)計(jì)時(shí)支持,例如"查找所有引用"

  • 運(yùn)行時(shí)支持,例如"斷點(diǎn)調(diào)試"

但是缺點(diǎn)也很明顯,手工編寫顯式映射是一項(xiàng)耗時(shí)并且枯燥的工作。

雖然可以使用工具(例如代碼生成器)自動(dòng)生成這些映射代碼,但是今天我們介紹一種更方便的方式。

Source Generators

上次我們已經(jīng)介紹過Source Generators,它可以在編譯時(shí)創(chuàng)建并添加到編譯中的代碼,而無需像代碼生成器那樣顯式生成大量冗余代碼。

因此,我們這次嘗試用Source Generators來自動(dòng)生成顯式映射代碼。

實(shí)現(xiàn)代碼如下:

[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包含F(xiàn)romType和ToType參數(shù),Source Generators為FromType生成ToXXX的擴(kuò)展方法,遍歷ToType對應(yīng)類的所有屬性并顯示映射。

使用示例

示例代碼如下:

[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,編譯后可以看到,自動(dòng)生成了ToUserDto方法:

運(yùn)行后測試,工作正常,成功!

結(jié)論

當(dāng)然,目前的功能與真正的AutoMapper還相差很遠(yuǎn)。

但是,如果你也希望在代碼中使用顯式映射,本文將是一個(gè)很好的起點(diǎn)。

如果你覺得這篇文章對你有所啟發(fā),請關(guān)注我的個(gè)人公眾號”My IO“,記住我!

總結(jié)

以上是生活随笔為你收集整理的Source Generators实现简版AutoMapper的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。