Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Automapper and mapping list within a complex object / nested mappings

I am having a heck of a time converting from older mapping standards to automapper.

Here are my classes

// Models
public class BaseModel
{
    public Int64 Id { get; set; }
    public Guid UniqueId { get; set; }
    public DateTime? CreateDate { get; set; }
    public DateTime? LastUpdate { get; set; }
} 

public class LibraryItemModel : BaseModel
{
    public string Name { get; set; }
    public string Description { get; set; }
    public string URL { get; set; }
    public bool IsActive { get; set; }
    public List<LibraryCategoryModel> Categories { get; set; }
}   

public class LibraryCategoryModel : BaseModel
{
    public string Description { get; set; }
}


// Entity Classes

public partial class LibraryItem
{
    public LibraryItem()
    {
        this.LibraryItemCategories = new HashSet<LibraryItemCategory>();
    }

    public long Id { get; set; }
    public System.Guid UniqueId { get; set; }
    public string Description { get; set; }
    public string URL { get; set; }
    public System.DateTime CreateDate { get; set; }
    public System.DateTime LastUpdate { get; set; }
    public bool IsActive { get; set; }
    public string Name { get; set; }

    public virtual ICollection<LibraryItemCategory> LibraryItemCategories { get; set; }
}
// comes from a ternary table in DB... many to many
public partial class LibraryItemCategory
{
    public long LibraryItemId { get; set; }
    public long LibraryCategoryId { get; set; }
    public System.DateTime CreateDate { get; set; }
    public System.DateTime LastUpdate { get; set; }

    public virtual LibraryCategory LibraryCategory { get; set; }
    public virtual LibraryItem LibraryItem { get; set; }
}

public partial class LibraryCategory
{
    public LibraryCategory()
    {
        this.LibraryCategoryRoles = new HashSet<LibraryCategoryRole>();
        this.LibraryItemCategories = new HashSet<LibraryItemCategory>();
    }

    public long id { get; set; }
    public System.Guid UniqueId { get; set; }
    public string Description { get; set; }
    public System.DateTime CreateDate { get; set; }
    public System.DateTime LastUpdate { get; set; }

    public virtual ICollection<LibraryCategoryRole> LibraryCategoryRoles { get; set; }
    public virtual ICollection<LibraryItemCategory> LibraryItemCategories { get; set; }
}


    // Old Conversion code doing it the long way

    private LibraryItemModel Convert(Entities.LibraryItem libraryItem)
    {

        var newLibraryItem = new LibraryItemModel
        {
            UniqueId = libraryItem.UniqueId,
            Name = libraryItem.Name,
            Description = libraryItem.Description,
            URL = libraryItem.URL,
            CreateDate = libraryItem.CreateDate,
            LastUpdate = libraryItem.LastUpdate,
            IsActive = libraryItem.IsActive,
            Categories = new List<LibraryCategoryModel>()
        };

        foreach (var lc in libraryItem.LibraryItemCategories)
        {
            var newCategory = new LibraryCategoryModel
            {
                UniqueId = lc.LibraryCategory.UniqueId,
                Description = lc.LibraryCategory.Description,
                CreateDate = lc.LibraryCategory.CreateDate,
                LastUpdate = lc.LibraryCategory.LastUpdate
            };

            newLibraryItem.Categories.Add(newCategory);
        }

        return newLibraryItem;
    }


    // My attempt at automapper to go between the models and entities
    Mapper.CreateMap<EF.Entities.LibraryItem, LibraryItemModel>();
    Mapper.CreateMap<LibraryItemModel, EF.Entities.LibraryItem>();
          .ForMember(lim => lim.LibraryItemCategories, o => o.Ignore()
    Mapper.CreateMap<EF.Entities.LibraryCategory, LibraryCategoryModel>();
    Mapper.CreateMap<LibraryCategoryModel, EF.Entities.LibraryCategory>()
          .ForMember(lcm => lcm.LibraryCategoryRoles, o => o.Ignore())
          .ForMember(lcm => lcm.LibraryItemCategories, o => o.Ignore());

No matter how I configure ignores or custom mappings it seems to not like this nesting. Any Automapper experts out there who could tell me how a mapping with a complex object like this could be done. The enitity classes are being generated via an EF6 edmx file.

like image 361
DRobertE Avatar asked Jul 17 '14 17:07

DRobertE


2 Answers

So basically the problem here is that you want to map from each LibraryItemCategory that belongs to a LibraryItem to a LibraryCategoryModel that includes properties from each LibraryItemCatalog's LibraryCatalog property.

First you want to correctly map the collections to each other:

Mapper.CreateMap<LibraryItem, LibraryItemModel>()
    .ForMember(
        dest => dest.Categories, 
        opt => opt.MapFrom(src => src.LibraryItemCategories));

Next you need to worry about mapping each LibraryItemCategory inside of LibraryItem.LibraryItemCategories to a LibraryCatalogModel. As stated in the problem, you need to access each LibraryItemCategory's LibraryCatalog property and actually map from that instead. The way this looks is:

Mapper.CreateMap<LibraryItemCategory, LibraryCategoryModel>()
    .ConstructUsing(ct => Mapper.Map<LibraryCategoryModel>(ct.LibraryCategory))
    .ForAllMembers(opt => opt.Ignore());

Here, we're telling AutoMapper that to map from a LibraryItemCategory to a LibraryCategoryModel, we need to construct LibraryCategoryModel's using another call to Mapper.Map on the inner LibraryCategory property.

Next, all that's left to do is define the mapping from LibraryCategory to LibraryCategoryModel:

Mapper.CreateMap<LibraryCategory, LibraryCategoryModel>();

Now a call to Mapper.Map on the LibraryItem should take care of everything for you.


Alternatively, you could remove the map from LibraryItemCategory to LibraryCategoryModel and use LINQ to create the collection of LibraryCategorys that you actually want to map from in the mapping definition from LibraryItem to LibraryItemModel:

Mapper.CreateMap<LibraryItem, LibraryItemModel>()
    .ForMember(
        dest => dest.Categories, 
        opt => opt.MapFrom(
            src => src.LibraryItemCategories.Select(lb => lb.LibraryCategory)));

You'd obviously still need the mapping from LibraryCategory to LibraryCategoryViewModel, but you might prefer this since it involves fewer mappings.

like image 166
Andrew Whitaker Avatar answered Sep 22 '22 16:09

Andrew Whitaker


Try the something like

Mapper
    .CreateMap<LibraryItemModel, EF.Entities.LibraryItem>()
    .ForMember(
        dest => dest.LibraryItemCategories,
        opt => opt.MapFrom(src => src.LibraryItemCategories )
    );

so you declare where your nested property will be mapped.

You can find another example on the documentation site

like image 32
pollirrata Avatar answered Sep 19 '22 16:09

pollirrata