Map list to existing list in Automapper using a key

眉间皱痕 提交于 2019-12-10 15:04:13

问题


Automapper easily handles mapping one list of object types to another list of different objects types, but is it possible to have it map to an existing list using an ID as a key?


回答1:


I have not found better way than the following.

Here are source and destination.

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; }
}

Define converter (You should change List<> to whatever type you are using).

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;
    }
}

And here is actual mapping configuration and example.

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);

You should get the following result.




回答2:


I found this article very useful and as such I thought I would feed back in my generic version of the type converter which you can use to select the property to match on from each object.

Using it all you need to do is:

// 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;
    }
}


来源:https://stackoverflow.com/questions/11413247/map-list-to-existing-list-in-automapper-using-a-key

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!