如何为AutoMapper实现自定义命名解析器

本文关键字:自定义 AutoMapper 实现 | 更新日期: 2023-09-27 18:18:05

如何使用AutoMapper 映射下面的类而不显式指示所有成员映射:

public class Source
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    ... (huge amount of other properties)
}

类:

public class Destination
{
    public string Imie { get; set; }
    public string Nazwisko { get; set; }
    ... (huge amount of other properties)
}

使用翻译类:

var translations = new Dictionary<string, string>()
{
    { "FirstName", "Imie" },
    { "LastName", "Nazwisko" },
    ... (huge amount of other translations)
}

如何为AutoMapper实现自定义命名解析器

你可以这样做。

考虑以下方法:

public void CreateMapBasedOnDictionary<TSource, TDestination>(IDictionary<string, string> mapping_dictionary)
{
    var mapping_expression = AutoMapper.Mapper.CreateMap<TSource, TDestination>();
    foreach (var kvp in mapping_dictionary)
    {
        string source_property_name = kvp.Key;
        string destination_property_name = kvp.Value;
        Type member_type = typeof (TSource).GetProperty(source_property_name).PropertyType;
        mapping_expression = mapping_expression.ForMember(destination_property_name, x =>
        {
            typeof (IMemberConfigurationExpression<TSource>)
                .GetMethod("MapFrom", new []{typeof(string)})
                .MakeGenericMethod(member_type)
                .Invoke(x, new[] { source_property_name });
        });
    }
}

然后你可以这样使用:

var translations = new Dictionary<string, string>()
{
    {"FirstName", "Imie"},
    {"LastName", "Nazwisko"},
};
CreateMapBasedOnDictionary<Source, Destination>(translations);
Source src = new Source()
{
    FirstName = "My first name",
    LastName = "My last name"
};
var dst = AutoMapper.Mapper.Map<Destination>(src);

以下是CreateMapBasedOnDictionary方法的解释:

AutoMapper已经重载了ForMember,允许您按名称指定目标属性。我们在这里很好。

它也有一个超载的MapFrom,允许您指定名称的源属性。然而,这个重载的问题是,它需要属性类型的泛型参数(TMember)。

我们可以通过使用反射来获取属性的类型,然后使用适当的TMember类型参数动态调用MapFrom方法来解决这个问题。

一种方法是使用反射:

private TDestination Map<TSource, TDestination>(TSource source, Dictionary<string, string> mapData)
        where TSource : class
        where TDestination : class, new()
{
    if (source == null) return null;
    if (mapData == null) mapData = new Dictionary<string, string>();
    TDestination destination = new TDestination();
    PropertyInfo[] sourceProperties = typeof(TSource).GetProperties();
    foreach (PropertyInfo property in sourceProperties)
    {
        string destPropertyName = mapData.ContainsKey(property.Name) ? mapData[property.Name] : property.Name;
        PropertyInfo destProperty = typeof(TDestination).GetProperty(destPropertyName);
        if (destProperty == null) continue;
        destProperty.SetValue(destination, property.GetValue(source));
    }
    return destination;
}

你可以这样调用它:

Destination mapped = Map<Source, Destination>(source, translations);

感谢@yacoub-massad的回答,我做了这个解决方案:

var map = Mapper.CreateMap<Source, Destination>();
var sourceProperties = typeof(Source).GetProperties();
foreach (var sourceProperty in sourceProperties)
{
    var sourcePropertyName = sourceProperty.Name;
    var destinationPropertyName = translations[sourceProperty.Name];
    map.ForMember(destinationPropertyName, mo => mo.MapFrom<string>(sourcePropertyName));
}
var src = new Source() { FirstName = "Maciej", LastName = "Lis" };
var dest = Mapper.DynamicMap<Destination>(src);