简体   繁体   中英

AutoMapper ResolveUsing using generics cause “Value supplied is of type X but expected Y” error

I have a scenario where I have to map a DTO to a domain object using AutoMapper. This mapping needs some complex managment so I have created a Custom Value Resolver. I want to get Foo from FooDTO . The only difference between these two objects is that Foo have a ICollection<Child> and FooDTO have a TrackedData<ChildDTO> , where TrackeData<T> is an object with 3 ICollection<T> . This is my simplified scenario:

public class TrackedData<T>
{
    public ICollection<T> Updated { get; set; }
    public ICollection<T> Created { get; set; }
    public ICollection<T> Deleted { get; set; }

    public TrackedData()
    {
        Updated = new List<T>();
        Created = new List<T>();
        Deleted = new List<T>();
    }
}

public class Foo
{
    private List<Child> _childs;

    public string Name { get; set; }
    public ICollection<Child> Childs
    {
        get
        {
            if (_childs == null)
                _childs = new List<Child>();
            return _childs;
        }
        set
        {
            _childs = (List<Child>)value;
        }
    }
}

public class Child
{
    public string Description { get; set; }
}

public class FooDTO
{
    public string Name { get; set; }
    public TrackedData<ChildDTO> Childs { get; set; }
}

public class ChildDTO
{
    public string Description { get; set; }
}

public class Program
{
    public static void Main(string[] args)
    {
        Mapper.CreateMap(typeof(TrackedData<>), typeof(TrackedData<>));
        Mapper.CreateMap<ChildDTO, Child>();
        Mapper.CreateMap<FooDTO, Foo>()
            .ForMember(d => d.Childs, opt => opt.ResolveUsing<TrackedDataResolver<ChildDTO, Child>>());

        FooDTO fooDTO = CreateFakeFooDTO();

        Foo foo = Mapper.Map<FooDTO, Foo>(fooDTO);
    }

    private static FooDTO CreateFakeFooDTO() { /* omitted for simplicity */ }

    private static TrackedData<ChildDTO> CreateFakeTrackedDataChildDTO() { /* omitted for simplicity */ }

    private static ICollection<ChildDTO> CreateFakeListChildDTO() { /* omitted for simplicity */ }

    private static ChildDTO CrateFakeChildDTO(){ /* omitted for simplicity */ }
}

public class TrackedDataResolver<TSource, TDestination> : ValueResolver<TrackedData<TSource>, ICollection<TDestination>>
{
    protected override ICollection<TDestination> ResolveCore(TrackedData<TSource> source)
    {
        TrackedData<TDestination> trackedDestination = new TrackedData<TDestination>();
        ICollection<TDestination> destination = new List<TDestination>();

        trackedDestination = Mapper.Map<TrackedData<TSource>, TrackedData<TDestination>>(source);

        foreach (var o in trackedDestination.Created)
        {
            // Do stuff...
            destination.Add(o);
        }
        foreach (var o in trackedDestination.Updated)
        {
            // Do stuff...
            destination.Add(o);
        }
        foreach (var o in trackedDestination.Deleted)
        {
            // Do stuff...
            destination.Add(o);
        }

        return destination;
    }
}

When this line is executed

Foo foo = Mapper.Map<FooDTO, Foo>(fooDTO);

I get this error:

{"Value supplied is of type AutoMapperDemo.FooDTO but expected AutoMapperDemo.TrackedData'1[AutoMapperDemo.ChildDTO]. Change the value resolver source type, or redirect the source value supplied to the value resolver using FromMember.\\r\\nValue supplied is of type AutoMapperDemo.FooDTO but expected AutoMapperDemo.TrackedData'1[AutoMapperDemo.ChildDTO].Change the value resolver source type, or redirect the source value supplied to the value resolver using FromMember."}

Source code is avaiable in Github: https://github.com/josmonver/AutoMapperValueResolverIssue.git Any idea why am I getting this error?

Instead of bringing heavy logic into your mapper you may implement something like UnionAll method (Updated + Created + Deleted) in your TrackedData<T> class and map it:

    Mapper.CreateMap<FooDTO, Foo>()
        .ForMember(d => d.Childs, opt => opt.MapFrom(src => src.UnionAll()));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM