AutoMapper不会将计算字段映射到标量

时间:2012-11-15 22:04:14

标签: asp.net-mvc-3 automapper

我正在尝试将我的MVC3项目分成适当的DAL / Domain / ViewModel架构,但我遇到了AutoMapper的问题,并将计算字段从我的域映射到我的视图模型。

以下是我正在尝试做的一个例子:

接口

public interface IRequirement
{
    int Id { get; set; }
    ... bunch of others
    public decimal PlanOct { get; set; }
    public decimal PlanNov { get; set; }
    public decimal PlanDec { get; set; }
    ... and so on
    decimal PlanQ1 { get; }
    ... etc
    decimal PlanYear { get; }
    ... repeat for ActualOct, ActualNov ... ActualQ1 ... ActualYear...
}

域名模型

public class Requirement : IRequirement
{
    public int Id { get; set; }
    ... bunch of others
    public decimal PlanOct { get; set; }
    public decimal PlanNov { get; set; }
    public decimal PlanDec { get; set; }
    ... and so on
    public decimal PlanQ1 { get { return PlanOct + PlanNov + PlanDec; } }
    ... etc
    public decimal PlanYear { get { return PlanQ1 + PlanQ2 + PlanQ3 + PlanQ4; } }
    ... repeat for ActualOct, ActualNov ... ActualQ1 ... ActualYear...
}

还有VarianceX属性,即VarianceOct,计算方式为(PlanOct - ActualOct)等。

我的视图模型看起来几乎完全相同,除了计算字段,它具有默认的getter / setter语法,例如:

public decimal PlanQ1 { get; set; }

我在Global.asax中的AutoMapper配置如下所示:

Mapper.CreateMap<Domain.Abstract.IRequirement, Models.Requirement.Details>();

这对除计算的之外的所有属性都可以正常工作。我的计算字段(即* Q1,* Q2,* Q3,* Q4,*年和所有Variance *字段)都没有实际映射 - 它们都显示为默认值0.00。

我对此非常难过,而且我也是这个和AutoMapper的新手,所以也许我错过了一些东西。我的直觉是,由于属性签名不相同(即域对象只有非默认的getter而没有setter,而视图模型有默认的getter和setter),所以AutoMapper没有把它拿起来。但我也这样做了:

Mapper.CreateMap<Domain.Abstract.IRequirement, Models.Requirement.Details>()
            .ForMember(dest => dest.PlanQ1, opt => opt.MapFrom(src => src.PlanQ1);

它仍然解决为0.我也在调试器中确认了这一点。

我做错了什么?

提前致谢。

编辑1

按照Wal的建议我运行了测试并且它工作正常,所以我开始一步一步地向后工作,首先将Field1 / Field2 / Field3部分粘贴到接口/域/视图模型类中并验证它是否有效我的控制器,然后一次改变一件事。我发现,因为我正在处理十进制类型,如果我硬编码整数或双值,那么我得到零,但如果我转换为十进制或使用十进制文字,那么它的工作原理。但是,只有我手动设置它们,而不是从数据库中提取值。

换句话说,这是有效的(即PlanQ1 = 6):

var D = new Requirement { PlanOct = (decimal) 1.0, PlanNov = (decimal) 2.0, PlanDec = (decimal) 3.0 };
var V = Mapper.Map<IRequirement, Details>(D);

这有效:

var D = new Requirement { PlanOct = 1M, PlanNov = 2M, PlanDec = 3M };
var V = Mapper.Map<IRequirement, Details>(D);

但这不是(从存储库对象中提取单个域对象,而后者又使用Entity Framework从SQL Server中提取):

var D = requirementRepository.Requirement(5);
var V = Mapper.Map<IRequirement, Details>(D);

以上所有我得到的是PlanQ1和PlanYear的0。我在域对象(D)中验证了PlanOct = 1,PlanNov = 2和PlanDec = 3。我还验证了所有对象中的类型,包括EF生成的对象,是十进制的,SQL Server类型是十进制的。我甚至尝试映射到创建的视图模型,只是为了排除规则,我仍然为PlanQ1和PlanYear得到0:

var D = requirementRepository.Requirement(5);
var V = new Details();
Mapper.Map<IRequirement, Details>(D, V);

3 个答案:

答案 0 :(得分:2)

PlanQ1IRequirement的成员吗?你暗示它是你的最后一段代码片段,但如果不是,那么你将完全按照你的描述获得行为。

考虑一个简单的例子,说明你在做什么:

public interface IFoo
{
    string Field1 { get; set; }
    string Field2 { get; set; }
    //string Field3 { get; }
}

public class Foo1 : IFoo
{
    public string Field1 { get; set; }
    public string Field2 { get; set; }
    public string Field3 { get { return Field1 + Field2; } }
}
public class Foo2
{
    public string Field1 { get; set; }
    public string Field2 { get; set; }
    public string Field3 { get; set; }
}

注意在这个例子中我从界面中省略了Field3;现在当我运行以下内容时,映射失败

[TestMethod]
public void Map()
{
    Mapper.CreateMap<IFoo, Foo2>();
    var foo1 = new Foo1() { Field1 = "field1", Field2 = "field2" };
    var foo2 = new Foo2();
    Mapper.Map(foo1, foo2);
    Assert.AreEqual("field1field2", foo2.Field3);//fails, not mapped
}

因此,如果我在Field3 IFoo发表评论,那么一切都会有效。用你的代码检查这个简化的例子。

答案 1 :(得分:2)

考虑@Wal帖子,试试这个地图,

Mapper.CreateMap<IFoo, Foo2>()
    .ForMember(destination => destination.Field3, options => options.MapFrom(source => source.Field1 + source.Field2));

并且

[TestMethod]
public void Map()
{
    Mapper.CreateMap<IFoo, Foo2>()
        .ForMember(destination => destination.Field3, options => options.MapFrom(source => source.Field1 + source.Field2));
    var foo1 = new Foo1() { Field1 = "field1", Field2 = "field2" };
    var foo2 = new Foo2();
    Mapper.Map(foo1, foo2);
    Assert.AreEqual("field1field2", foo2.Field3); // True
}

答案 2 :(得分:1)

刚刚意识到这个没有答案,所以我想关闭它。从技术上讲,它没有答案,因为出于某种原因,我无法让Automapper在这种情况下发挥出色。我最后做的是返回并在我的存储库中创建几个映射方法,一个用于将DAL对象的单个实例映射到IRequirement对象,另一个用于映射集合。然后在存储库中而不是调用Mapper.Map我只是调用我的自定义映射方法,它完美地工作。

我仍然不明白为什么这不起作用,但我遇到了其他一些Automapper刚刚抛出的类,我必须手动映射至少一个或两个字段,尽管Automapper确实需要处理其余的那些情况。

我确定有一些关于它的东西我还没有看到。但无论如何,回到部分或完全手动映射是我的解决方法。