Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mapping a property of custom type, inheriting from IEnumerable #635

Closed
carl-berg opened this issue Dec 1, 2014 · 11 comments
Closed

Mapping a property of custom type, inheriting from IEnumerable #635

carl-berg opened this issue Dec 1, 2014 · 11 comments

Comments

@carl-berg
Copy link

I have a problem mapping a custom type, that inherits from an interface that in turn inherits from IEnumerable. I am trying to map the property using a resolver, because it has a custom constructor but i keep getting a mapping error when trying to map this. The error goes away if i take away the inheritance to IEnumerable so I assume it has something to do with this.
I have created an issue for this at stack overflow.
My model looks like this:

public interface IMyEnumerable<T> : IEnumerable<T> { }
public class MyIEnumerable<T> : IMyEnumerable<T>
{
    private readonly IEnumerable<T> _items;

    public MyIEnumerable(IEnumerable<T> items)
    {
        _items = items;
    }

    public IEnumerator<T> GetEnumerator()
    {
        return _items.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

public class Source
{
    public List<SourceItem> Items { get; set; }
}

public class Destination
{
    public IMyEnumerable<DestinationItem> Items { get; set; }
}

public class SourceItem
{
    public string Name { get; set; }
}

public class DestinationItem
{
   public string Name { get; set; }
}

My mappings looks like this:

public class MyResolver : ValueResolver<Source, IMyEnumerable<DestinationItem>>
{
    protected override IMyEnumerable<DestinationItem> ResolveCore(Source source)
    {
        var destinationItems = Mapper.Map<IEnumerable<SourceItem>, IEnumerable<DestinationItem>>(source.Items);
        return new MyIEnumerable<DestinationItem>(destinationItems);
    }
}
Mapper.CreateMap<Source, Destination>()
    .ForMember(x => x.Items, m => m.ResolveUsing<MyResolver>());

Mapper.CreateMap<SourceItem, DestinationItem>();

The error i get looks like this:

Mapping types:
MyIEnumerable`1 -> IMyEnumerable`1
MyIEnumerable`1[[DestinationItem]] -> IMyEnumerable`1[[DestinationItem]]

Destination path:
Destination.Items.Items

Source value:
MyIEnumerable`1[DestinationItem]
  ----> System.ArgumentException : Object of type System.Collections.Generic.List`1[DestinationItem] cannot be converted to type IMyEnumerable`1[DestinationItem].
@carl-berg
Copy link
Author

With some debugging i can see that the difference between IMyEnumerable<T> : IEnumerable<T> and just IMyEnumerable is that automapper seems to use an EnumerableMapper when the destinationtype inherits from IEnumerable and an AssignableMapper if not. I can see that i get matches both from EnumerableMapper and AssignableMapper when things go wrong, but EnumerableMapper comes first in the list and is chosen because of that...

@jbogard
Copy link
Member

jbogard commented Dec 6, 2014

Why not use a custom type converter?

@carl-berg
Copy link
Author

In my real world problem i have properties of the source object that i need to construct my list, so it's not a clear cut one list to another mapping. Is this still possible with a type converter? If so, could you show me how?

@jbogard
Copy link
Member

jbogard commented Dec 9, 2014

Can you modify your types to be a bit closer to what you're trying to achieve?

@mwpowellhtx
Copy link

I can't speak to how AutoMapper treats to/from type coercion, i.e. treating sources/destinations as IEnumerable, IEnumerable<T>, IList<T>, etc, but I can say that if interesting edge cases are necessary, TypeConverter is pretty simple to work with. You can type-convert as deep or as shallow into the collection/items/members as you need to go, invoking additional mappings as needs be. HTH

@carl-berg
Copy link
Author

This is roughly my scenario:

public class Source
{
    public int PageSize { get; set; }
    public int Page { get; set; }
    public string OrderBy { get; set; }
    public bool SortAscending { get; set; }
    public IEnumerable<SourceItem> Items { get; set; }
}

public class Destination
{
    public IMyEnumerable<DestinationItem> Items { get; set; }
}

public class MyEnumerable<T> : IMyEnumerable<T>
{
    public MyEnumerable(T items, int pageSize, int page, string orderBy, bool sortAscending){ ... }
    public int PageSize { get; private set; }
    public int Page { get; private set; }
    public string OrderBy { get; private set; }
    public bool Ascending { get; private set; }

    // .. Enumeration implementation etc ..
}

public interface IMyEnumerable<T> : IEnumerable<T>
{
    int PageSize { get; }
    int Page { get;  }
    string OrderBy { get; }
    bool Ascending { get; }
}

...and there are some more properties that maps between source and destination...

@mwpowellhtx
Copy link

It will be more rewarding if you just tried it.

@carl-berg
Copy link
Author

I did manage to get something working by doing a mapping like this, which seems acceptable i guess. How would you guys solve this yourselves?

Mapper.CreateMap<Source, Destination>()
      .ForMember(x => x.Items, m => m.MapFrom(x => x));

Mapper.CreateMap<Source, IMyEnumerable<DestinationItem>>()
      .ConvertUsing(x => /* creating my implementation here */);

Mapper.CreateMap<SourceItem, DestinationItem>();

@jbogard
Copy link
Member

jbogard commented Dec 10, 2014

It's a weird problem, you've got a rearranging of members. I don't know of an easy way to handle this, the names match up but are in different places.

@carl-berg
Copy link
Author

OK, I'm satisfied with that though. I just wanted to know if there was an obvious way to handle this that I didn't know about. The destination interface is a common type in our codebase so I created a generic typeconverter for it and with some extension methods and method chaining I've got something I can reuse pretty easily. Thanks for your time and input!

@lock
Copy link

lock bot commented May 8, 2019

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot locked as resolved and limited conversation to collaborators May 8, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants