AutoMapper實現模塊化注冊自定義擴展MapTo<>()
我們都知道AutoMapper是使用的最多的實體模型映射,如果沒有AutoMapper做對象映射那么我們需要想一下是怎么寫的,是不是很麻煩寫起來很難受這種,自從有了AutoMapper我們的代碼量是不是減少了很多,但是.NetCore中的AutoMapper需要配置Profile文件,但是我們這個每次都需要在Starup中去注冊,這就很麻煩了,下面我這篇文章來講一下我們不需要每次去注冊,讓他實現自動注入而不需要每次都手動注入。
上次回顧
在上篇文章講了Sukt.Core框架的一個整體架構搭建和怎樣實現模塊化注冊和批量注入,如果沒有看過上篇文章的請查看【從我做起[原生DI實現模塊化和批量注入].Net Core 之一】,本篇來講進行AutoMapper的模塊化注冊;
階段一
上篇文章有講到我們有一個自動注冊的基類,那么我們這次來回顧一下上次的依賴注入也是重寫的這個自動注冊基類他就是SuktAppModuleBase,我們的AutoMapper類也是繼承與SuktAppModuleBase然后重寫它里面的方法;我們的基類方法里面其實就是Startup中的兩個方法;我們首先先創建一個Sukt.Core.AutoMapper類庫,里面包含一個類SuktMapperModuleBase類繼承我們的SuktAppModuleBase,我們重寫基類里面的方法在此之前我們先看一下自定義擴展,這個擴展有什么作用呢?其實他的作用就是相同字段不需要配置Profile文件,而不需要我們去進行構造函數注入或者創建一個靜態對象,每次使用都要注入或者創建一個靜態對象會很煩;所講的不需要在構造函數注入或者創建靜態對象;而是直接自定義擴展方法使用你的對象名稱(點)出來的;我們先看使用例子
使用范例:
public async Task<bool> InsertAsync(DataDictionaryInputDto input) { input.NotNull(nameof(input)); var entity = input.MapTo<DataDictionaryEntity>(); }
階段二
在進行自動化注冊之前我們需要在Sukt.Core.Shared中添加我們的AutoMapper擴展類這個擴展類就是我們的AutoMapperExtension擴展方法我們可以看一下這里面的源代碼,主要包括以下這些SetMapper()、CheckMapper()、MapTo<TTarget>()、MapTo<TSource, TTarget>()、IEnumerable<TTarget> MapToList<TTarget>()、IQueryable<TOutputDto> ToOutput<TOutputDto>()方法;SetMapper(IMapper mapper)方法主要用來進行對象實例注入、CheckMapper()方法用來檢測實例有沒有創建成功;自定義擴展方法分為這些,具體里面的代碼怎么實現下面;廢話少說;直接上代碼。
private static IMapper _mapper = null; /// <summary> /// 寫入AutoMapper實例 /// </summary> public static void SetMapper(IMapper mapper) { mapper.NotNull(nameof(mapper)); _mapper = mapper; } /// <summary> /// 檢查傳入的實例 /// </summary> private static void CheckMapper() { _mapper.NotNull(nameof(_mapper)); } /// 將對象映射為指定類型 /// </summary> /// <typeparam name="TTarget">要映射的目標類型</typeparam> /// <param name="source">源對象</param> /// <returns>目標類型的對象</returns> public static TTarget MapTo<TTarget>(this object source) { CheckMapper(); source.NotNull(nameof(source)); return _mapper.Map<TTarget>(source); } /// <summary> /// 使用源類型的對象更新目標類型的對象 /// </summary> /// <typeparam name="TSource">源類型</typeparam> /// <typeparam name="TTarget">目標類型</typeparam> /// <param name="source">源對象</param> /// <param name="target">待更新的目標對象</param> /// <returns>更新后的目標類型對象</returns> public static TTarget MapTo<TSource, TTarget>(this TSource source, TTarget target) { CheckMapper(); source.NotNull(nameof(source)); target.NotNull(nameof(target)); return _mapper.Map(source, target); } /// <summary> /// 將數據源映射為指定<typeparamref name="TTarget"/>的集合 /// </summary> /// <typeparam name="TTarget">動態實體</typeparam> /// <param name="sources">數據源</param> /// <returns></returns> public static IEnumerable<TTarget> MapToList<TTarget>(this IEnumerable<object> sources) { CheckMapper(); sources.NotNull(nameof(sources)); return _mapper.Map<IEnumerable<TTarget>>(sources); } /// <summary> /// 將數據源映射為指定<typeparamref name="TOutputDto"/>的集合 /// </summary> /// <param name="source">數據源</param> /// <param name="membersToExpand">成員展開</param> public static IQueryable<TOutputDto> ToOutput<TOutputDto>(this IQueryable source,params Expression<Func<TOutputDto, object>>[] membersToExpand) { CheckMapper(); return _mapper.ProjectTo<TOutputDto>(source, membersToExpand); }
以上就是自定義擴展方法,使用了這個之后我們不需要在使用AutoMapper的地方使用靜態類或者注入屬性了;
階段三
現在看下實現AutoMapper模塊化注冊;上面說到了我們創建了一個Sukt.Core.AutoMapper類庫在這個類庫下面有一個SuktMapperModuleBase類,我們繼承了SuktAppModuleBase類,現在重寫基類里面ConfigureServices方法,先看下代碼;
/// <summary> /// 重寫SuktAppModuleBase /// </summary> /// <param name="service"></param> /// <returns></returns> public override IServiceCollection ConfigureServices(IServiceCollection service) { var assemblyFinder = service.GetOrAddSingletonService<IAssemblyFinder, AssemblyFinder>(); var assemblys = assemblyFinder.FindAll(); var suktAutoMapTypes = assemblys.SelectMany(x => x.GetTypes()).Where(s => s.IsClass && !s.IsAbstract && s.HasAttribute<SuktAutoMapperAttribute>(true)).Distinct().ToArray(); service.AddAutoMapper(mapper => { this.CreateMapping<SuktAutoMapperAttribute>(suktAutoMapTypes, mapper); },assemblys,ServiceLifetime.Singleton); var mapper = service.GetService<IMapper>();//獲取autoMapper實例 AutoMapperExtension.SetMapper(mapper); return base.ConfigureServices(service); }
使用靜態擴展方法主要的代碼其實這些代碼;
var mapper = service.GetService<IMapper>();//獲取autoMapper實例
AutoMapperExtension.SetMapper(mapper);
service.AddAutoMapper(mapper =>{ this.CreateMapping<SuktAutoMapperAttribute>(suktAutoMapTypes, mapper);},assemblys,ServiceLifetime.Singleton);
到這里我們還不算完成,因為還有一些代碼沒有實現;只寫了擴展方法但是它怎么知道要轉給誰呢;我們就要用到特性了
創建一個SuktAutoMapperAttribute特性這個名字不要和官方的相同,不然會沖突,特性里面包含一下代碼,還有一些代碼就不貼了,具體代碼請看Github【Sukt.Core】和【Destiny.Core.Flow】
/// <summary> /// 如果使用AutoMapper會跟官方沖突,所以在前面加了項目代號 /// </summary> [AttributeUsage(AttributeTargets.Class)] public class SuktAutoMapperAttribute:Attribute { /// <summary> /// 構造函數傳值 /// </summary> /// <param name="targetTypes"></param> public SuktAutoMapperAttribute(params Type[] targetTypes) { targetTypes.NotNull(nameof(targetTypes)); TargetTypes = targetTypes; } /// <summary> /// 類型數組 /// </summary> public Type[] TargetTypes { get; private set; } public virtual SuktAutoMapDirection MapDirection { get { return SuktAutoMapDirection.From | SuktAutoMapDirection.To; } } }
我們在SuktMapperModuleBase類中在添加一個靜態方法,方法代碼;
/// <summary> /// 創建擴展方法 /// </summary> /// <typeparam name="TAttribute"></typeparam> /// <param name="sourceTypes"></param> /// <param name="mapperConfigurationExpression"></param> private void CreateMapping<TAttribute>(Type[] sourceTypes, IMapperConfigurationExpression mapperConfigurationExpression)where TAttribute : SuktAutoMapperAttribute { foreach (var sourceType in sourceTypes) { var attribute = sourceType.GetCustomAttribute<TAttribute>(); if (attribute.TargetTypes?.Count() <= 0) { return; } foreach (var tatgetType in attribute.TargetTypes) { ///判斷是To if (attribute.MapDirection.HasFlag(SuktAutoMapDirection.To)) { mapperConfigurationExpression.CreateMap(sourceType, tatgetType); } ///判斷是false if (attribute.MapDirection.HasFlag(SuktAutoMapDirection.From)) { mapperConfigurationExpression.CreateMap(tatgetType, sourceType); } } } }
使用的時候在我們的類上配置SuktAutoMapperAttribute要映射到那個對象;看下我的使用方法我這里的是把DataDictionaryInputDto轉到DataDictionaryEntity類上;

這里有一點坑哦AutoMapper此方法僅支持相同字段映射;非相同字段還是需要自己寫這個文件的,這個文件直接創建一個自己的類繼承與AutoMapper的Profile就好,如果使用這個文件則不需要配置上面的特性;這里也會在程序運行時注入到容器內不需要管理實例;

到這里我們的AutoMapper模塊化注冊和自定義擴展已經完成了,
給個星星! ⭐️
如果你喜歡這篇文章或者它幫助你, 請給 在Github上給Star~(辛苦星咯)
GitHub
Sukt.Core:https://github.com/GeorGeWzw/Sukt.Core
Destiny.Core.Flow:https://github.com/GeorGeWzw/Destiny.Core.Flow
