[英]Automapper mapping multiple lists from source to single list on destination
[英]Mapping multiple source properties to a single destination property
我想知道是否有辦法用一些自定義類型或值解析器來處理這種情況。
public class SuperDateTime
{
public DateTimeOffset Date { get; set; }
public string Timezone { get; set; }
}
public class Entity
{
public DateTimeOffset CreationDate { get; set; }
public string CreationDateZone { get; set; }
public DateTimeOffset EndDate { get; set; }
public string EndDateZone { get; set; }
}
public class Model
{
public SuperDateTime CreationDate { get; set; }
public SuperDateTime EndDate { get; set; }
}
當我在目標對象中有SuperDateTime
時,我想使用關聯的DateTimeOffset
和源對象中的時區string
來實例化此對象。
當然,我想做的是制作通用的東西,所以不要想到每個Entity
每個CreateMap
中的MapFrom
我嘗試使用自定義TypeConverter,但它只支持SourceType - > DestinationType在我的情況下,我有一個string
和DateTimeOffset
,必須創建一個SuperDateTime
除了LiamK所建議的,下一個可能的改進是編寫一個輔助方法來做.MapFrom
。 根據您的要求,它可以是簡單的也可以是復雜的。 我將提供一個簡單的假設,但您可以修改和優化它以滿足您的可能需求。
static IMappingExpression<TFrom, TTo> MapSuperDateTime<TFrom, TTo>(
this IMappingExpression<TFrom, TTo> expression,
Expression<Func<TTo, object>> dest)
{
var datePropertyName = ReflectionHelper.FindProperty(dest).Name;
var timezomePropertyName = datePropertyName + "Zone";
var fromType = typeof (TFrom);
var datePropertyGetter = fromType.GetProperty(datePropertyName).ToMemberGetter();
var timezonePropertGetter = fromType.GetProperty(timezomePropertyName).ToMemberGetter();
return expression.ForMember(dest, opt => opt.MapFrom(src => new SuperDateTime
{
Date = (DateTimeOffset)datePropertyGetter.GetValue(src),
Timezone = (string)timezonePropertGetter.GetValue(src)
}));
}
然后你可以像這樣指定你的映射:
Mapper.CreateMap<Entity, Model>()
.MapSuperDateTime(dest => dest.CreationDate)
.MapSuperDateTime(dest => dest.EndDate);
假設是,如果你的實體DateTimeOffset
被稱為喇嘛,那么你相應的實體string
被稱為blaZone,和你的模型SuperDateTime
被稱為喇嘛。
您可以使用客戶解析器。 我使用自定義解析器來獲取int這樣的對象;
讓我們說你正在創建這樣的映射(Althoug你沒有展示你是如何創建它的):
Mapper.CreateMap<YourSource, YourDestination>()
.ForMember(x => x.DateTimeOffset, opt => opt.ResolveUsing(new DateTimeOffsetResolver(loadRepository)).FromMember(x => x.timezone));
這就是你的解析器的樣子:
public class DateTimeOffsetResolver : ValueResolver<string, DateTimeOffset>
{
private DatabaseLoadRepository loadRepository;
public personIdResolver(DatabaseLoadRepository repo)
{
this.loadRepository = repo;
}
protected override DateTimeOffset ResolveCore(string timeZone)
{
//Your logic for converting string into dateTimeOffset goes here
return DateTimeOffset; //return the DateTimeOffset instance
}
}
如果不需要訪問,可以刪除與Nhibernate Repository相關的所有代碼。 您可以在此處進一步了解自定義解析器
你問的簡答題是'不',沒有辦法使用自定義值解析器來映射<string,DateTimeOffset> => SuperDateTime並避免重復的.MapFrom調用。 在上面的示例中,這樣的值解析器無法區分映射期間哪些字符串和DateTimeOffsets在一起。
不確定您是否自己擁有.MapFrom代碼,但如果沒有,以下是您問題的最佳解決方案:
Mapper.CreateMap<Entity, Model>()
.ForMember(
dest => dest.CreationDate,
opt => opt.MapFrom(
src => new SuperDateTime()
{
Date = src.CreationDate,
TimeZone = src.CreationDateZone
};
));
如果你真的想避免過多的MapFrom聲明,看看是否有辦法在這里利用映射繼承。
編輯:修改SuperDateTime的實例化以匹配提供的源代碼。
在睡覺之后,這里有一種感覺更通用的替代方案。
假設,你想做這樣的事情:
Mapper.CreateMap<Entity, Model>()
.ForMemberType((member,src) => new SuperDateTime
{
Date = (DateTimeOffset)GetPropertyValue(src, member),
Timezone = (string)GetPropertyValue(src, member+"Zone")
});
這看起來比我的第一個答案好一些,因為在這里您指定要立即映射SuperDateTime
所有成員。 (該類型是從lambda的返回類型推斷出來的。)實際上,類似於AutoMapper已經擁有的ForAllMembers
。 您無法使用標准memberOptions
作為IMemberConfigurationExpression<TSource>
的唯一問題是不允許您訪問當前正在配置的成員。 為簡潔起見,我完全從ForMemberType
簽名中刪除了memberOptions
參數,但如果需要,可以很容易地將其添加回來(也就是設置其他一些選項 - 請參見此處的示例)。
所以為了能夠編寫上面所有你需要的是GetPropertyValue
方法,它看起來像這樣:
public object GetPropertyValue(object o, string memberName)
{
return o.GetType().GetProperty(memberName).ToMemberGetter().GetValue(o);
}
和ForMemberType
方法本身,看起來像這樣:
public static IMappingExpression<TSource, TDestination> ForMemberType<TSource, TDestination, TMember>(
this IMappingExpression<TSource, TDestination> expression,
Func<string, TSource, TMember> memberMapping
)
{
return new TypeInfo(typeof(TDestination))
.GetPublicReadAccessors()
.Where(property => property.GetMemberType() == typeof(TMember))
.Aggregate(expression, (current, property)
=> current.ForMember(property.Name,
opt => opt.MapFrom(src => memberMapping(property.Name, src))));
}
就是這樣。 為避免每次重新編譯屬性getter,您可能希望添加一個非常簡單的緩存層,為每種類型編譯一次(執行ToMemberGetter
)並在某處記住結果。 使用AutoMapper自己的DictonaryFactory
然后IDictionary.GetOrAdd
可能是最直接的方式:
private readonly IDictionary<string, IMemberGetter> _getters
= new DictionaryFactory().CreateDictionary<string, IMemberGetter>();
public object GetPropertyValue(object o, string memberName)
{
var getter = _getters.GetOrAdd(memberName + o.GetType().FullName, x => o.GetType()
.GetProperty(memberName).ToMemberGetter());
return getter.GetValue(o);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.