AutoMapper和在复杂对象中映射列表/嵌套映射

10

我正在努力将旧的映射标准转换为Automapper,但遇到了困难。

以下是我的类:

// 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());
无论我怎样配置忽略或自定义映射,似乎都不喜欢这种嵌套。有没有自动映射专家可以告诉我如何完成像这样具有复杂对象的映射。实体类是通过EF6 edmx文件生成的。

无论我怎样配置忽略或自定义映射,似乎都不喜欢这种嵌套。有没有自动映射专家可以告诉我如何完成像这样具有复杂对象的映射。实体类是通过EF6 edmx文件生成的。


当你说“似乎不喜欢”时,你的意思是什么?它是没有映射属性还是出现了某种错误? - Andrew Whitaker
1
你说得对,“似乎不喜欢”这个词并不清楚。问题的根源是,LibraryItemModel有一个LibraryCategoryModels列表,但在LibraryItem实体中没有任何映射到它的内容,因为该实体具有LibraryItemCategories...请注意,在foreach循环中,它被嵌套包含另一个foreach,其中我从LibraryItem中的LibraryItemCategories列表创建另一个LibraryCategory...我正在寻找一种方法来映射LibraryItemModel中的Categories属性,以便它填充为从LibraryItemCategories列表创建的LibraryCategory对象。明白了吗? - DRobertE
好的,明白了,谢谢。 - Andrew Whitaker
我认为需要一些特殊的投影或自定义解析器,但我不知道该如何处理这个问题... LibraryItemCategories 本质上是一个多对多三元数据库表,这就是为什么实体被生成成它们现在的样子。 - DRobertE
2个回答

13

基本上,这里的问题是您想要从每个属于LibraryItemLibraryItemCategory映射到一个LibraryCategoryModel,该模型包括来自每个LibraryItemCatalogLibraryCatalog属性。

首先,您需要正确地将这些集合进行映射:

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

接下来您需要担心的是将LibraryItem.LibraryItemCategories中的每个LibraryItemCategory映射到LibraryCatalogModel。正如问题所述,您需要访问每个LibraryItemCategoryLibraryCatalog属性,然后从那里进行实际映射。代码如下:

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

在这里,我们告诉 AutoMapper 从 LibraryItemCategory 映射到 LibraryCategoryModel,需要使用 Mapper.Map 在内部的 LibraryCategory 属性上进行另一个调用来构造 LibraryCategoryModel

接下来,剩下要做的就是定义从 LibraryCategoryLibraryCategoryModel 的映射:

Mapper.CreateMap<LibraryCategory, LibraryCategoryModel>();

现在对LibraryItem调用Mapper.Map方法,将为您处理所有事情。


或者,您可以从LibraryItemCategoryLibraryCategoryModel的映射中移除映射,并使用LINQ创建您实际想要从中映射的LibraryCategory集合,然后在从LibraryItemLibraryItemModel的映射定义中使用它们:

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

显然你仍然需要从LibraryCategoryLibraryCategoryViewModel的映射,但你可能更喜欢这种方式,因为它涉及较少的映射。


然而,我刚刚尝试了您的替代建议,似乎效果很好!但是我不确定替代方案是如何工作的,而原始建议没有起作用,因为替代方案可以很好地捕获每个字段,包括描述、ID和唯一ID。 - DRobertE
由于我熟悉 Lambda 表示法,这个替代方案看起来非常简单。它的意思是“从 LibraryItemCategories 中选择每个 LibraryCategory 并将它们放入 dest.Categories”。 - DRobertE
不,你必须双向配置映射。 - Andrew Whitaker
糟糕...不过还是谢谢!我现在不需要另一个方向,因为我不会进行保存,但我认为它应该是这里的反向。 - DRobertE
谢谢,ConstructUsing 正是我所需要的! - Michel Amorosa
显示剩余5条评论

2

尝试类似以下的操作

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

所以你声明嵌套属性将被映射的位置。

您可以在文档网站上找到另一个示例。


在源代码中没有LibraryItemCategories,只有LibraryItemCategories。 - DRobertE

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接