Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AutoMapper many to many relationship into collection

Tags:

automapper

I'm trying to map entities into a collection through a many to many table using AutoMapper.

My domain model (Entity Framework) looks like this:

public class User
{
    public int UserID { get; set; }
    public string Name { get; set; }        
    public IList<UserCompany> UserCompanies { get; set; }
}

public class Company
{
    public int CompanyID { get; set; }
    public string Name { get; set; }
}

public class UserCompany
{
    public int UserCompanyID { get; set; }
    public int UserID { get; set; }
    public int CompanyID { get; set; }
}

I'm trying to map to a class that looks like this:

public class CompanyDTO
{
    public int CompanyID { get; set; }
    public string Name { get; set; }
}

public class UserDTO
{
    public int UserID { get; set; }
    public string Name { get; set; }
    public IList<CompanyDTO> Companies { get; set; }
}

My current mapping configuration looks like this:

Mapper.CreateMap<Company, CompanyDTO>(); //works no problem

Mapper.CreateMap<User, UserDTO>()
    .ForMember( dto => dto.Companies,
    opt => opt.MapFrom(x => Mapper.Map<IList<Company>, IList<CompanyDTO>>(x.UserCompanies.Select( y => y.Company ).ToList())));

Asserting that the configuration is valid returns true, but when I try to actually map a User to a UserDTO, I get: Signature of the body and declaration in a method implementation do not match.

If I use AfterMap and manually move over each Company into the Companies list, it will work, but it seems like I should be able to handle this within create map.

In my query to get a single user from the DB I am including the UserCompany.Company navigation property and I can quickwatch and see that there is being returned.

like image 944
UmmmActually Avatar asked Aug 02 '12 17:08

UmmmActually


1 Answers

You don't need to have the explicit map in your mapping configuration. You should be able to do something like:

Mapper.CreateMap<User, UserDTO>()
    .ForMember(dto => dto.Companies, opt => opt.MapFrom(x => x.UserCompanies));

You'll also need to define a mapping for UserCompany:

Mapper.CreateMap<UserCompany, CompanyDTO>();

Note that you don't have a CompanyDTO class in your examples so I can't determine the actual mapping configuration.

Update

I presume there is a reason why you need an IList in your User entity rather than simply an IList. Given that, I believe you need a custom resolver: https://github.com/AutoMapper/AutoMapper/wiki/Custom-value-resolvers

Update 2

I'm glad you have it sorted. For completeness, heres the example should you decide to use a custom ValueResolver with the classes above.

Mapper.CreateMap<Company, CompanyDTO>();
Mapper.CreateMap<User, UserDTO>()
    .ForMember(dto => dto.Companies, opt => opt.ResolveUsing<CompanyResolver>());
Mapper.AssertConfigurationIsValid();

Where CompanyResolver is something like

public class CompanyResolver : ValueResolver<User, IList<CompanyDTO>>
{
    protected override IList<CompanyDTO> ResolveCore(User source)
    {
        return source.UserCompanies
            .Select(userCompany =>
                    Mapper.Map<Company, CompanyDTO>(companies.FirstOrDefault(x => x.CompanyID == userCompany.CompanyID)))
            .ToList();
    }
}
like image 135
Mightymuke Avatar answered Oct 23 '22 04:10

Mightymuke