3

MVC3プロジェクトを適切なDAL/ドメイン/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プロパティもあります。つまり、(PlanOct-ActualOct)として計算されるVarianceOctなどです。

私のビューモデルは、計算フィールドの代わりにデフォルトのゲッター/セッター構文​​を持っていることを除いて、ほとんど同じように見えます。次に例を示します。

public decimal PlanQ1 { get; set; }

Global.asaxのAutoMapper構成は次のようになります。

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

これは、計算されたプロパティを除くすべてのプロパティで正常に機能します。計算されたフィールド(つまり、* Q1、* Q2、* Q3、* Q4、* Year、およびすべてのVariance *フィールド)は実際にはマップされていません。これらはすべてデフォルト値の0.00で表示されます。

私はこれにかなり困惑していて、これとAutoMapperの初心者でもあるので、何かを逃したかもしれません。私の直感では、プロパティのシグネチャは同一ではないため(つまり、ドメインオブジェクトにはデフォルト以外のゲッターとセッターがあり、ビューモデルにはデフォルトのゲッターとセッターがあります)、AutoMapperはそれを取得しません。しかし、私もこれを行いました:

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

そして、それでも0に解決されました。デバッガーでもこれを確認しました。

私は何が間違っているのですか?

前もって感謝します。

編集1

Walのアドバイスに従った後、テストを実行して機能したので、一度に1ステップずつ逆方向に作業を開始し、最初にField1 / Field2/Field3パーツをinterface/domain / viewモデルクラスに貼り付け、コントローラーで機能することを確認しました。次に、一度に1つずつ変更します。私が見つけたのは、小数型を扱っているので、整数値または倍精度値でハードコーディングするとゼロになりますが、小数にキャストするか、小数リテラルを使用すると機能するということです。ただし、データベースから値を取得する場合ではなく、手動で設定した場合に限ります。

言い換えれば、これは機能します(つまり、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);

ただし、これは行いません(リポジトリオブジェクトから単一のドメインオブジェクトをプルし、エンティティフレームワークを使用して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で生成されたオブジェクトを含むすべてのオブジェクトの型が10進数であり、SQLServerの型が10進数であることを確認しました。それを除外するために、作成したビューモデルにマッピングしようとしましたが、PlanQ1とPlanYearでは0になります。

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

3 に答える 3

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
}
于 2012-11-16T01:46:14.997 に答える
2

PlanQ1メンバーIRequirementですか?あなたはそれがあなたの最後のコードスニペットによるものであることを暗示していますが、そうでない場合、あなたはあなたが説明した通りの振る舞いを得るでしょう。

あなたがしていることの簡単な例を考えてみましょう:

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
}

だから私がコメントするとField3IFooすべてが再び機能します。この簡略化された例をコードで確認してください。

于 2012-11-15T23:29:58.933 に答える
1

これが答えられないままになっていることに気づいたので、私はそれを閉じたいと思いました。技術的には、何らかの理由でこのシナリオでAutomapperを適切に再生させることができなかったため、回答がありません。最終的には、リポジトリ内に2つのマッピングメソッドを作成しました。1つはDALオブジェクトの単一インスタンスをIRequirementオブジェクトにマップし、もう1つはコレクションをマップします。次に、リポジトリでMapper.Mapを呼び出す代わりに、カスタムマッピングメソッドを呼び出すだけで、完全に機能します。

なぜこれが機能しないのかはまだわかりませんが、Automapperがスローする他のいくつかのクラスに遭遇し、Automapperが残りの部分を処理しますが、少なくとも1つまたは2つのフィールドを手動でマップする必要があります。それらの場合。

まだ見たことがない何かがあると思います。しかし、いずれにせよ、部分的または完全に手動のマッピングにフォールバックすることが私の回避策でした。

于 2013-01-15T15:37:17.380 に答える