自动映射从源和输出引用的同一模型的对象



我想通过自动从模型映射到视图模型。我的视图模型看起来像这样,模型(FooBarRecord(几乎没有额外的属性,bar属性是相同的:

public class FooBarVm
{
    public int id { get; set; }
    public BarRecord bar { get; set; }
    public string foo { get; set; }
    public id barbaz_id { get; set; }
    public string barbaz_name { get; set; }
}
public class FooBarRecord
{
    public int id { get; set; }
    public BarRecord bar { get; set; }
    public string foo { get; set; }
    public int foo2 { get; set; }
    public BarBazRecord barbaz { get; set; }
}
public class BarRecord
{
    public int id { get; set; }
    public BarBazRecord baz { get; set; }
    public string bar1 { get; set; }
    public int bar2 { get; set; }
}
public class BarBazRecord
{
    public int id { get; set; }
    public string name { get; set; }
}

问题出在条形列中。我的自动映射器配置如下所示:

Mapper.CreateMap<FooBarRecord, FooBarVm>()
    .ForMember(s => s.bar, m => m.MapFrom(rec => rec.bar))
    //other columns - those are OK
;

我也尝试了其他几种方法,比如定义从BarRecordBarRecord的映射,或者用rec代替rec.bar和从FooBarRecord映射到BarRecord。我发现唯一可行的解决方案是展平BarRecord,将其所有属性添加到FooBarVm而不是单个引用 - 但这很丑陋且难以维护,绝对不是我想要的。

编辑:这些类只是示例,真实的类具有更多属性,但我已经检查过它们没问题。可能起到一定作用的是,在BarRecord的属性中有一个对象,所以我将其添加为BarBazRecord

错误消息如下:

The following 4 properties on Foo.Bar.ViewModels.FooBarVm are not mapped:
bar1
bar2
id
name

这些都是BarRecordBarBazRecord的非对象属性。我试图像这样解决它们:

.ForMember(s => s.bar.bar1, m => m.MapFrom(rec => rec.bar.bar1))

甚至忽略它们:

.ForMember(s => s.bar.bar1, opt => opt.Ignore())

但错误消息刚刚更改为"System.ArgumentException:表达式的 => s.poskytovatel.ulice' 必须解析为顶级成员。我确认工作的唯一解决方案是从第一条错误消息中添加整个列表(在我的真实情况下有数十个属性(,只是为了必须将代码从bar = vm.bar;更改为bar = GetBar(vm.bar_id); 这是一种解决方法,但我更愿意学习适当的解决方案。

编辑2:即使解决方法也失败了:-(无论我做什么,我的第一个错误仍然存在。我将bar更改为bar_id将所有BarRecord属性添加到FooBarVm。然后我尝试映射新属性(m.MapFrom(rec => rec.bar.bar1)(或忽略它们(opt => opt.Ignore()(,甚至从FooBarVm和自动映射器配置中删除对BarRecord的任何引用。没有任何变化 - FooBarRecord中不直接存在的所有属性都列为未映射,即使忽略或从嵌套模型中映射也是如此。向FooBarRecord添加虚拟属性显然不是正确的方法,尽管这是证明会影响错误消息的唯一方法,除了用其他错误覆盖它。

我更新了模型 - BarBazRecord也引用自FooBarRecordFooBarVm包含从BarBazRecord映射的属性。这些属性似乎映射正确,它们不包含在列表或未映射的属性中。那就更奇怪了。

我将其作为答案发布,因为它太大而无法发表评论。 在当前形式中,使用 Automapper 4.2,您的映射应该可以正常工作(见下文(。 我所做的唯一更改是将barbaz_id定义为int,而不是id,以便编译代码。 因此,对于以下类型:

public class FooBarVm {
    public int id { get; set; }
    public BarRecord bar { get; set; }
    public string foo { get; set; }
    public int barbaz_id { get; set; }
    public string barbaz_name { get; set; }
}
public class FooBarRecord {
    public int id { get; set; }
    public BarRecord bar { get; set; }
    public string foo { get; set; }
    public int foo2 { get; set; }
    public BarBazRecord barbaz { get; set; }
}
public class BarRecord {
    public int id { get; set; }
    public BarBazRecord baz { get; set; }
    public string bar1 { get; set; }
    public int bar2 { get; set; }
}
public class BarBazRecord {
    public int id { get; set; }
    public string name { get; set; }
}

定义映射:

Mapper.CreateMap<FooBarRecord, FooBarVm>();

定义源:

var src = new FooBarRecord { bar = new BarRecord { bar1 = "b1", bar2 = 23, 
                                                   baz = new BarBazRecord { id = 5, 
                                                                           name = "nme" }, 
                                                   id = 2 }, 
                             barbaz = new BarBazRecord { name = "nee", 
                                                         id = 3 }, 
                             foo = "fooo", foo2 = 123, id = 99 };

并将其映射到目的地:

var dest = Mapper.Map<FooBarVm>(src);

所有字段均已映射,没有任何错误。 如果您正在做不同的事情,则应更新您的问题以使错误可重现。 或者,您可能需要检查是否使用的是最新版本的映射器。

问题出在另一个映射中,但我也需要稍后解决它。最后,正确使用ForAllMembers(opt => opt.Ignore()起到了作用。

Mapper.CreateMap<FooBarRecord, FooBarVm>().ForAllMembers(opt => opt.Ignore());
Mapper.CreateMap<FooBarRecord, FooBarVm>()
    .ForMember(s => s.bar, m => m.MapFrom(rec => rec.bar))
    //other columns - those are OK
;

最新更新