不同命名空間/程式集下擁有相同屬性名稱、型別的類別(如 DTO 與 Entity、Web Service 代理類與領域模型),需頻繁互轉。本文實作通用 ObjectMapper,基於反射自動匹配可讀/可寫同名同型屬性,消除手工賦值樣板代碼。
public static class ObjectMapper {
// 屬性映射描述
public class PropertyMapper {
public PropertyInfo SourceProperty { get; set; }
public PropertyInfo TargetProperty { get; set; }
}
// 獲取可映射屬性對:同名、可讀/可寫、型別完全一致
public static IList<PropertyMapper> GetMapperProperties(Type sourceType, Type targetType) {
var srcProps = sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
var tgtProps = targetType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
return (from s in srcProps
join t in tgtProps on s.Name equals t.Name
where s.CanRead && t.CanWrite && s.PropertyType == t.PropertyType
select new PropertyMapper { SourceProperty = s, TargetProperty = t }).ToList();
}
// 執行複製
public static void CopyProperties(object source, object target) {
if (source == null || target == null) return;
var mappers = GetMapperProperties(source.GetType(), target.GetType());
foreach (var m in mappers) {
var value = m.SourceProperty.GetValue(source);
m.TargetProperty.SetValue(target, value);
}
}
}
// 來源:Web Service 代理類
var dto = new Jasen.Core.Test.Info { Id = 1, Name = "Test", Amount = 100.5m, Created = DateTime.Now };
// 目標:領域模型
var entity = new Jasen.Core.Info();
ObjectMapper.CopyProperties(dto, entity);
// entity.Id=1, entity.Name="Test", entity.Amount=100.5m, entity.Created=DateTime.Now
// 1. 屬性名稱映射 Attribute
[AttributeUsage(AttributeTargets.Property)]
public class MapFromAttribute : Attribute { public string SourceName { get; set; } }
// 2. 型別轉換器
public interface IValueConverter {
object Convert(object value, Type targetType);
}
public class DateTimeToStringConverter : IValueConverter {
public object Convert(object value, Type targetType)
=> value is DateTime dt ? dt.ToString("yyyy-MM-dd") : value;
}
// 3. 擴展 GetMapperProperties 讀取 Attribute、註冊 Converter
private static readonly ConcurrentDictionary<Tuple<Type, Type>, IList<PropertyMapper>> _cache
= new ConcurrentDictionary<Tuple<Type, Type>, IList<PropertyMapper>>();
public static IList<PropertyMapper> GetMapperPropertiesCached(Type source, Type target)
=> _cache.GetOrAdd(Tuple.Create(source, target), _ => GetMapperProperties(source, target));
| 庫 | 特點 | 適用場景 |
|---|---|---|
| AutoMapper | 慣例配置、Profile、投影、條件映射、單元測試友善 | 複雜領域模型、DTO 多層轉換、團隊標準化 |
| Mapster | 編譯時生成代碼、極高效能、零反射 | 高頻映射、效能敏感、.NET Core 專案 |
| ExpressMapper | Expression Tree 編譯、輕量、支援巢狀映射 | 中型專案、需自定義邏輯 |
| 本文 ObjectMapper | 零依賴、極簡、同名同型直映、易理解擴展 | 輕量專案、臨時遷移、教學演示、禁止引入 NuGet 環境 |