使用密钥将列表映射到Automapper中的现有列表

时间:2012-07-10 12:24:01

标签: c# automapper automapper-2

Automapper可以轻松处理将一个对象类型列表映射到另一个不同对象类型的列表,但是是否可以使用ID作为键将其映射到现有列表?

2 个答案:

答案 0 :(得分:7)

我没有找到比以下更好的方法。

以下是来源和目的地。

public class Source
{
    public int Id { get; set; } 
    public string Foo { get; set; }
}

public class Destination 
{ 
    public int Id { get; set; }
    public string Foo { get; set; }
}

定义转换器(您应该将List<>更改为您正在使用的任何类型)。

public class CollectionConverter: ITypeConverter<List<Source>, List<Destination>>
{
    public List<Destination> Convert(ResolutionContext context)
    {
        var destinationCollection = (List<Destination>)context.DestinationValue;
        if(destinationCollection == null)
            destinationCollection = new List<Destination>();
        var sourceCollection = (List<Source>)context.SourceValue;
        foreach(var source in sourceCollection)
        {
            Destination matchedDestination = null;

            foreach(var destination in destinationCollection)
            {
                if(destination.Id == source.Id)
                {
                    Mapper.Map(source, destination);
                    matchedDestination = destination;
                    break;
                }
            }
            if(matchedDestination == null)
                destinationCollection.Add(Mapper.Map<Destination>(source));
        }
        return destinationCollection;
    }
}

这是实际的映射配置和示例。

Mapper.CreateMap<Source,Destination>();
Mapper.CreateMap<List<Source>,List<Destination>>().ConvertUsing(new CollectionConverter());

var sourceCollection = new List<Source>
{
    new Source{ Id = 1, Foo = "Match"},
    new Source{ Id = 2, Foo = "DoesNotMatchWithDestination"}
};
var destinationCollection = new List<Destination>
{
    new Destination{ Id = 1, Foo = "Match"},
    new Destination{ Id = 3, Foo = "DoeNotMatchWithSource"}
};
var mergedCollection = Mapper.Map(sourceCollection, destinationCollection);

您应该得到以下结果。

Mapping result

答案 1 :(得分:1)

我发现这篇文章非常有用,因此我认为我会反馈我的通用版本的类型转换器,您可以使用它来选择要匹配的属性。

使用它你需要做的就是:

// Example of usage
Mapper.CreateMap<UserModel, User>();
var converter = CollectionConverterWithIdentityMatching<UserModel, User>.Instance(model => model.Id, user => user.Id);
Mapper.CreateMap<List<UserModel>, List<User>>().ConvertUsing(converter);

//The actual converter
public class CollectionConverterWithIdentityMatching<TSource, TDestination> : 
    ITypeConverter<List<TSource>, List<TDestination>> where TDestination : class
{
    private readonly Func<TSource, object> sourcePrimaryKeyExpression;
    private readonly Func<TDestination, object> destinationPrimaryKeyExpression;

    private CollectionConverterWithIdentityMatching(Expression<Func<TSource, object>> sourcePrimaryKey, Expression<Func<TDestination, object>> destinationPrimaryKey)
    {
        this.sourcePrimaryKeyExpression = sourcePrimaryKey.Compile();
        this.destinationPrimaryKeyExpression = destinationPrimaryKey.Compile();
    }

    public static CollectionConverterWithIdentityMatching<TSource, TDestination> 
        Instance(Expression<Func<TSource, object>> sourcePrimaryKey, Expression<Func<TDestination, object>> destinationPrimaryKey)
    {
        return new CollectionConverterWithIdentityMatching<TSource, TDestination>(
            sourcePrimaryKey, destinationPrimaryKey);
    }

    public List<TDestination> Convert(ResolutionContext context)
    {
        var destinationCollection = (List<TDestination>)context.DestinationValue ?? new List<TDestination>();
        var sourceCollection = (List<TSource>)context.SourceValue;
        foreach (var source in sourceCollection)
        {
            TDestination matchedDestination = default(TDestination);

            foreach (var destination in destinationCollection)
            {
                var sourcePrimaryKey = GetPrimaryKey(source, this.sourcePrimaryKeyExpression);
                var destinationPrimaryKey = GetPrimaryKey(destination, this.destinationPrimaryKeyExpression);

                if (string.Equals(sourcePrimaryKey, destinationPrimaryKey, StringComparison.OrdinalIgnoreCase))
                {
                    Mapper.Map(source, destination);
                    matchedDestination = destination;
                    break;
                }
            }

            if (matchedDestination == null)
            {
                destinationCollection.Add(Mapper.Map<TDestination>(source));
            }
        }

        return destinationCollection;
    }

    private string GetPrimaryKey<TObject>(object entity, Func<TObject, object> expression)
    {
        var tempId = expression.Invoke((TObject)entity);
        var id = System.Convert.ToString(tempId);
        return id;
    }
}