开发者

Can AutoMapper create a map for an interface and then map with a derived type?

开发者 https://www.devze.com 2023-02-01 10:21 出处:网络
I have an interface IFoo: public interface IFoo { int Id { get; set; } } And then a concrete implementation:

I have an interface IFoo:

public interface IFoo
{
    int Id { get; set; }
}

And then a concrete implementation:

public class Bar : IFoo
{
    public int Id { get; set; }
    public string A { get; set; }
}

public class Baz : IFoo
{
    public int Id { get; set; }
    public string B { get; set; }
}

I'd like to be able to map all IFoo but specifying their derived type instead:

Mapper.CreateMap<int, IFoo>().AfterMap((id, foo) => foo.Id = id);

And then map (without explicitly creating maps for Bar and Baz):

var bar = Mapper.Map<int, Bar>(123);
// bar.Id == 123

var baz = Mapper.Map<int, Baz>(456);
// baz.Id == 456

But this doesn't work in 1.1. I know I could specify all开发者_如何学编程 Bar and Baz but if there are 20 of these, I'd like to not have to manage them and rather just have what I did above for creating the map. Is this possible?


I've found a solution to this problem. I created an implementation of an IObjectMapper:

// In this code IIdentifiable would be the same as IFoo in the original post.

public class IdentifiableMapper : IObjectMapper
{
    public bool IsMatch(ResolutionContext context)
    {
        var intType = typeof(int);
        var identifiableType = typeof(IIdentifiable);

        // Either the source is an int and the destination is an identifiable.
        // Or the source is an identifiable and the destination is an int.
        var result = (identifiableType.IsAssignableFrom(context.DestinationType) && intType == context.SourceType) || (identifiableType.IsAssignableFrom(context.SourceType) && intType == context.DestinationType);
        return result;
    }

    public object Map(ResolutionContext context, IMappingEngineRunner mapper)
    {
        // If source is int, create an identifiable for the destination.
        // Otherwise, get the Id of the identifiable.
        if (typeof(int) == context.SourceType)
        {
            var identifiable = (IIdentifiable)mapper.CreateObject(context);
            identifiable.Id = (int)context.SourceValue;
            return identifiable;
        }
        else
        {
            return ((IIdentifiable)context.SourceValue).Id;
        }
    }
}

This is great but it needs to be registered to the mapper registry. It needs to go at the beginning of the list since we want to catch all int/IIdentifiable source/destination combinations (this is placed in configuration such as Global.asax):

var allMappers = MapperRegistry.AllMappers();

MapperRegistry.AllMappers = () => new IObjectMapper[]
{
    new IdentifiableMapper(),
}.Concat(allMappers);
0

精彩评论

暂无评论...
验证码 换一张
取 消