使用AutoMapper进行自定义映射

时间:2015-07-29 16:58:48

标签: c# .net mapping automapper

我有两个非常简单的对象:

public class CategoryDto
{
    public string Id { get; set; }

    public string MyValueProperty { get; set; }
}

public class Category
{
    public string Id { get; set; }

    [MapTo("MyValueProperty")]
    public string Key { get; set; }
}

使用AutoMapper将Category映射到CategoryDto时,我希望以下行为:

应该像往常一样映射属性,但具有MapTo属性的属性除外。在这种情况下,我必须读取Attribute的值来查找target属性。 source属性的值用于在destination属性中查找要注入的值(借助字典)。一个例子总是好于1000字......

示例:

Dictionary<string, string> keys = 
    new Dictionary<string, string> { { "MyKey", "MyValue" } };

Category category = new Category();
category.Id = "3";
category.Key = "MyKey";

CategoryDto result = Map<Category, CategoryDto>(category);
result.Id               // Expected : "3"
result.MyValueProperty  // Expected : "MyValue"

Key属性映射到MyValueProperty(通过MapTo属性),分配的值是&#34; MyValue&#34;,因为源属性值是&#34;的myKey&#34;它被映射(通过字典)到&#34; MyValue&#34;。

使用AutoMapper可以实现吗?我当然需要一个适用于每个对象的解决方案,而不仅仅是Category / CategoryDto。

3 个答案:

答案 0 :(得分:8)

我终于(经过这么多小时!!!!)找到了解决方案。 我与社区分享这个;希望它会帮助别人...

编辑:请注意,它现在更加简单(AutoMapper 5.0+),您可以像我在这篇文章中回答的那样:How to make AutoMapper truncate strings according to MaxLength attribute?

public static class Extensions
{
    public static IMappingExpression<TSource, TDestination> MapTo<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
    {
        Type sourceType = typeof(TSource);
        Type destinationType = typeof(TDestination);

        TypeMap existingMaps = Mapper.GetAllTypeMaps().First(b => b.SourceType == sourceType && b.DestinationType == destinationType);
        string[] missingMappings = existingMaps.GetUnmappedPropertyNames();

        if (missingMappings.Any())
        {
            PropertyInfo[] sourceProperties = sourceType.GetProperties();
            foreach (string property in missingMappings)
            {
                foreach (PropertyInfo propertyInfo in sourceProperties)
                {
                    MapToAttribute attr = propertyInfo.GetCustomAttribute<MapToAttribute>();
                    if (attr != null && attr.Name == property)
                    {
                        expression.ForMember(property, opt => opt.ResolveUsing(new MyValueResolve(propertyInfo)));
                    }
                }
            }
        }

        return expression;
    }
}

public class MyValueResolve : IValueResolver
{
    private readonly PropertyInfo pInfo = null;

    public MyValueResolve(PropertyInfo pInfo)
    {
        this.pInfo = pInfo;
    }

    public ResolutionResult Resolve(ResolutionResult source)
    {
        string key = pInfo.GetValue(source.Value) as string;
        string value = dictonary[key];
        return source.New(value);
    }
}

答案 1 :(得分:0)

使用IValueResolver和ResolveUsing()方法的实现应该相当简单。你基本上只需要为解析器提供一个接受属性信息的构造函数(或者如果你想要接受一个lambda表达式并解析类似于How to get the PropertyInfo of a specific property?的属性信息。虽然我还没有测试过它我自己想象以下内容会起作用:

AutoMapper.Mapper.CreateMap<Category, CategoryDto>()
    .ForMember(
         dest => dest.Value, 
         opt => opt.ResolveUsing(
              src => 
                   new PropertyBasedResolver(typeof(Category.Key) as PropertyInfo).Resolve(src)));

然后设置您需要执行的映射:

date

当然这是一个非常糟糕的lamda,我建议你通过让你的属性解析器根据属性/属性信息确定它应该查看的源对象的属性来清理它,这样你就可以通过一个干净的将新的PropertyBasedResolver(属性)放入ResolveUsing()中,但希望这足以让您走上正确的轨道。

答案 2 :(得分:0)

让我们假设我有以下课程

public class foo
{
  public string Value;
}
public class bar
{
    public string Value1;
    public string Value2;
}

您可以将lambda传递给ResolveUsing:

.ForMember(f => f.Value, o => o.ResolveUsing(b =>
{
    if (b.Value1.StartsWith("A"));)
    {
        return b.Value1;
    }
    return b.Value2;
}


 ));