如何使用AutoMapper将目标对象与源对象中的子对象进行映射?

时间:2016-02-23 08:59:50

标签: c# entity-framework automapper

我有这样的源和目标对象:

class ProductWithCategories // Source class
{
    public Product Product { get; set; } // Product is an EF entity class
    public IEnumerable<Category> Categories { get; set; }
}

class ProductViewModel // Dest class
{
    public int Id { get; set; }
    // Other properties with the same name as Product class

    public IEnumerable<CategoryViewModel> Categories { get; set; }
}

因此,我需要将source.Product的值映射到dest,然后将source.Categories映射到dest.Categories。是否可以使用AutoMapper?

我尝试了这一点,当它失败时我并不感到惊讶:

        config.CreateMap<ProductWithCategories, ProductViewModel>()
            .ForMember(q => q, option => option.MapFrom(q => q.Product))
            .ForMember(q => q.Categories, option => option.MapFrom(q => q.Categories));

以下是我收到的例外情况:

  

[AutoMapperConfigurationException:成员的自定义配置是   仅支持类型上的顶级个人成员。]

4 个答案:

答案 0 :(得分:24)

在与OP讨论之后,事实证明他的主要需求是将源对象内的子/嵌套对象快速映射到展平的目标对象。他不想为目的地的每个属性写一个映射。

以下是实现此目的的方法:

  • 定义映射Product - &gt; ProductViewModel用于展平产品
  • 的成员
  • 将地图Category定义为CategoryViewModel
  • 定义映射ProductWithCategories - &gt; ProductViewModel映射类别,然后在aftermap中映射Product

    config.CreateMap<ProductWithCategories, ProductViewModel>() .ForMember(q => q.Id, option => option.Ignore()) // flattened in AfterMap .ForMember(q => q.Categories, option => option.MapFrom(q => q.Categories)) .AfterMap((src, dst) => Mapper.Map(src.Product, dst));

答案 1 :(得分:8)

使用最新版本的AutoMapper,您可以执行以下操作:

config.CreateMap<Product, ProductViewModel>()
      .ForMember(q => q.Categories, option => option.Ignore());

config.CreateMap<ProductWithCategories, ProductViewModel>()
      .ConstructUsing(s => AutoMapper.Mapper.Map<ProductViewModel>(s.Product))
      .ForMember(q => q.Categories, option => option.MapFrom(q => q.Categories))
      .ForAllOtherMembers(o => o.Ignore();

ConstructUsing()用于从源的嵌套子[ren]生成和填充基类。如果您有多个这样的嵌套子级,则需要进行多次映射调用,将其中的每一个连续映射到第一次Map()调用生成的实例上。 .ForAllOtherMembers()是相对较新的(如果您没有它,请获取更新版本的AutoMapper。)不幸的是,它稍微不安全,就好像您添加了需要映射但忘记更新地图的目标成员,配置验证不会抓住它。

答案 2 :(得分:0)

你应该这样做 -

AutoMapper.Mapper.CreateMap<Category, CategoryViewModel>();
AutoMapper.Mapper.CreateMap<ProductWithCategories, ProductViewModel>()
     .ForMember(a => a.Id, b => b.ResolveUsing(c => c.Product != null ? c.Product.MyProperty : 0))
     .ForMember(a => a.Categories, b => b.ResolveUsing(c => c.Categories));

但最好将ProductViewModel(类似Id之类的道具)中的属性包装在另一个类中。并创建另一个地图,让事物以自动播放方式工作。

答案 3 :(得分:-1)

产生错误的违规行是

.ForMember(q => q.Id, option => option.MapFrom(q => q.Product.Id))
.ForMember(q => q.OtherProperty, option => option.MapFrom(q => q.Product.OtherProperty))

错误消息很难理解,但这意味着您必须明确说明目标属性:

Category

您还需要定义从CategoryViewModel.ForMember(q => q.Categories, option => option.MapFrom(q => q.Categories)) 的映射

config.CreateMap<Category, CategoryViewModel>();

工作:

var unitOfWorkProvider = new PetaPocoUnitOfWorkProvider();

using (var uow = unitOfWorkProvider.GetUnitOfWork("SomeConnectionString"))
{
    var errorNumber = new SqlParameter("@ErrorNumber", SqlDbType.Int) { Direction = ParameterDirection.Output };
    var errorSeverity = new SqlParameter("@ErrorSeverity", SqlDbType.Int) { Direction = ParameterDirection.Output };
    var errorState = new SqlParameter("@ErrorState", SqlDbType.Int) { Direction = ParameterDirection.Output };
    var errorProcedure = new SqlParameter("@ErrorProcedure", SqlDbType.NVarChar) { Direction = ParameterDirection.Output, Size = 128 };
    var errorLine = new SqlParameter("@ErrorLine", SqlDbType.Int) { Direction = ParameterDirection.Output };
    var errorMessage = new SqlParameter("@ErrorMessage", SqlDbType.NVarChar) { Direction = ParameterDirection.Output, Size = 4000 };

    var sqlScript = Sql.Builder.Append(";EXEC @0 " +
                   " @@Uid = @1," +
                   " @@ErrorNumber = @2 OUTPUT," +
                   " @@ErrorSeverity = @3 OUTPUT," +
                   " @@ErrorState = @4 OUTPUT," +
                   " @@ErrorProcedure = @5 OUTPUT," +
                   " @@ErrorLine = @6 OUTPUT," +
                   " @@ErrorMessage = @7 OUTPUT",
    "[Schema].[USP_BlaDiBla]",
    new Guid("1E454A42-CC41-4FA1-BE91-1F7689986A23").ToString(),
    errorNumber,
    errorSeverity,
    errorState,
    errorProcedure,
    errorLine,
    errorMessage
   );

   var result = UnitOfWork.Current.FirstOrDefault<SomeDto>(sqlScript);
}