Search code examples
asp.net-web-apiautomapper-2ablecommerce

AutoMapper Throwing Exception When Mapping Against Two Different Collection Types


I'm writing a ASP.NET Web API service which uses AutoMapper for object mapping. I'm building this service against the AbleCommerce shopping cart software. The service (destination) classes at issue track users and their addresses.

public class UserModel
{
    public int UserID { get; set; }
    ...
    public List<AddressModel> Addresses { get; set; }
}

public class AddressModel 
{
    public int AddressID { get; set; }
    public int UserID { get; set; }
    ...
    // Contains some of the fields from AbleCommerce Address
}

My sources are the User and Address classes in the AbleCommerce package. I have included the class declarations for the ancestor classes, because I believe there may be an issue with the use of the generic constraint, but I'm not completely sure.

public class User: IPersistable
{
    public int UserId { get; set; }
    ...
    public AddressCollection Addresses 
    {
        get
        {
            // Calls an internal method which creates a new instance
            // of AddressCollection and loads the addresses for the
            // user from the database.
            ...
            return _Addresses;
        }
    }
}

public class Address : IPersistable 
{ 
    public int AddressId { get; set; }
    public int UserId { get; set; }
    ... 
}

public class AddressCollection : PersistentCollection<Address> { ... }

public class PersistentCollection<T> : SortableCollection<T> where T : IPersistable { ... }

public interface IPersistable { ... }

Mapping from the AbleCommerce class to my service class works as expected. Mapping from my service class to the AbleCommerce class works, as long as I ignore the properties I don't need in my mapping configuration. However, in the case of the Addresses property, I don't know how to resolve the difference (from an AutoMapper perspective) between the AddressCollection in the AbleCommerce User class and the List<AddressModel> in my service's UserModel class.

When I test the mapping configuration, the exception I get is:

AutoMapper.AutoMapperConfigurationException : The following property on AlfredModel.Models.Classes.AddressModel cannot be mapped: 
    Addresses
Add a custom mapping expression, ignore, add a custom resolver, or modify the destination type AlfredModel.Models.Classes.AddressModel.
Context:
    Mapping to property Addresses from System.Object to AlfredModel.Models.Classes.AddressModel
    Mapping to property Addresses from CommerceBuilder.Users.AddressCollection to System.Collections.Generic.List`1[[AlfredModel.Models.Classes.AddressModel, AlfredModel, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]
    Mapping to property User from CommerceBuilder.Users.User to AlfredModel.Models.Classes.UserModel
    Mapping from type CommerceBuilder.Orders.Basket to AlfredModel.Models.Classes.BasketModel
Exception of type 'AutoMapper.AutoMapperConfigurationException' was thrown.
   at AutoMapper.ConfigurationStore.DryRunTypeMap(ICollection`1 typeMapsChecked, ResolutionContext context)
   at AutoMapper.ConfigurationStore.DryRunTypeMap(ICollection`1 typeMapsChecked, ResolutionContext context)
   at AutoMapper.ConfigurationStore.DryRunTypeMap(ICollection`1 typeMapsChecked, ResolutionContext context)
   at AutoMapper.ConfigurationStore.DryRunTypeMap(ICollection`1 typeMapsChecked, ResolutionContext context)
   at AutoMapper.ConfigurationStore.AssertConfigurationIsValid(IEnumerable`1 typeMaps)
   at AutoMapper.ConfigurationStore.AssertConfigurationIsValid()
   at AutoMapper.Mapper.AssertConfigurationIsValid()
   at AlfredModelTests.AlfredMapConfigurationTest.Maps_Configured_Correctly() in C:\Users\ntruick\Documents\Visual Studio 2010\Projects\AlfredModel\AlfredModelTests\AlfredMapConfigurationTest.cs:line 21

This is confusing to me because the AddressModel class does not have a property named Addresses. I'm clearly missing something. Any suggestions, advice, or clarifications would be greatly appreciated.


Solution

  • I believe I've discovered a solution. After much digging (and a few upvotes in appreciation), I found out I needed to define the mapping myself by creating a type converter for the user address collections:

    public class UserAddressesTypeConverter : 
        ITypeConverter<PersistentCollection<Address>, List<AddressModel>
    {
        var source = (PersistentCollection<Address>)context.SourceValue;
        var result = new List<AddressModel>();
    
        foreach (Address address in source.Cast<Address>().ToList()
            result.Add(Mapper.Map<AddressModel>(address));
    
        return result;
    }
    

    My confusion was based on the fact I was trying to accomplish something similar to this using the AddressCollection class directly. It appears that the generic constraint on PersistentCollection was not allowing the descendant class to correctly resolve to the Address type. Once I changed the source, it just required a simple LINQ Cast to make everything work out.