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
學不完的技術,寫不完的代碼。QQ群:773595012
▼-----------------------------------------▼-----------------------------------------▼