1

わかりましたので、最初の大規模な (っぽい) EF 4.1 POCO + MVC アプリケーションを構築しています。これはレガシー システムの置き換えであるため、既存のデータベースを使用しています。

DbContext T4 生成を使用して POCO クラスを生成しました。ボイラープレート コードを削減するために、MVC クラスの多くのセクシーなジェネリックを使用して、いくつかの非常に優れたフォームと非常に優れた検証が行われています。すべて問題ありません。

突然、(私にとって) 最も賢明なことは、ビジネス ロジックの一部を POCO オブジェクトのプロパティの一部の "セット" に含めることであることに気付きました。

たとえば、次のクラスが T4 によって生成されたとします。

public partial class SalesOrderLine
{
    public int ID { get; set; }
    public int SalesOrderID { get; set; }
    public int ProductID { get; set; }
    public decimal UnitPrice { get; set; }
    public int Quantity { get; set; }
    public decimal ExtendedPrice { get; set; }

    public virtual Product Product { get; set; }
    public virtual SalesOrder SalesOrder { get; set; }
}

計算フィールド「ExtendedPrice」をデータベースに保存するべきではないという明白な議論をしばらく無視して、私と一緒に乗ってください...

...そして、論理的には、このオブジェクトが実際に販売注文ラインを表すと想定されている場合、次の単体テストが機能するようにオブジェクトを構築できるはずです。

SalesOrderLine sol = new SalesOrderLine();
sol.UnitPrice = 100;
sol.Quantity = 5;
Assert.IsEqual(sol.ExtendedPrice, 500);

...明らかに、ベース POCO を T4 で生成したい限り、それはできません。私にはいくつかのオプションがあるようです:

  1. 生成されたコード ファイルのプロパティを「コンパイルしない」に設定し、生成されたコードをコピーして別のファイルに貼り付け、「設定」を変更して、UnitPrice または Quantity が設定されている場合に拡張価格を設定するビジネス ロジックを実行します。ここでの欠点は、オブジェクトがデータベースから読み込まれるたびにロジックが実行されることです (EF はプライベート フィールドではなくパブリック プロパティを設定するため)。さらに、このオブジェクトは、データベースの変更が発生した場合、プロジェクトの残りの期間にわたって手動で維持する必要があります。

  2. DbContext の SaveChanges() によって呼び出されるオブジェクト用の Validate ルーチンで呼び出される UpdateTotals 関数を作成します。明らかに、上記の単体テストはその場合には機能しません。ただし、システムと私の統合テストは機能し、オブジェクトに変更が加えられたときにのみコードを呼び出します。

  3. 間違った質問をしていると判断し、"SetPrice" と "SetQuantity" というオブジェクトにメソッドを実際に追加し、UnitPrice と Quantity のセット アクセサーを "内部" に限定する必要があると判断します。ここでの欠点は、MVC がフォームからモデルを更新しようとし、それらのプロパティを設定できないことです。

  4. 私がすでに持っているよりもさらに多くの抽象化レベルを作成する2つまたは3つのフレームワークをダウンロードすることを含むいくつかのソリューション...リポジトリパターン、または「NHibernateを使用する」など...あなたはこれを提案できますが、私は成長しています「学術的に正しい」方法で物事を設定するのにどれだけの労力がかかるかにうんざりしています。このプロジェクトでは、長期的な保守性と開発速度のスペクトルの中間に位置し、追加のツールや dll を大量に使用してプロジェクトを過度に複雑にしないようにしたいと考えています... ...しかし、私は心を開いてください:)

--- 編集: 別のアイデア ---

[5.] 別の考えとして、フィールドは常に単純に計算されるため、データベースまたはその他の方法で設定する必要はありません。したがって、次のようなものが機能する可能性があります。

 public decimal ExtendedAmount
 {
     get { return UnitPrice * Quantity; }
     internal set { }
 }

...私の考えでは、EFインスタンス化は「セット」を呼び出そうとしますが、セットは何もしません。その後、オブジェクトが保存または変更をチェックされると、「get」が呼び出され、計算された値であり、その値は DB に格納されます。ここでの唯一の欠点は、データベースの ExtendedAmount フィールドに誤った値が格納されているときに、オブジェクト モデルを使用してデータベースを検証しようとしたときです。ちょっと面倒くさいですが、面白いトリックになると思いました...実際、「セット」は、(値 != 単価 * 数量) の場合に例外をスローする可能性があります。

--- 編集終了 ---

この種のケースで他の人が何をしたかを知りたいと思っています。それはよくあることだと確信しています。多くのチュートリアルでは、「データベースから POCO クラスを生成する」ところまで行って、残りのプロジェクト開発はあなたに任せているようです。

乾杯、クリス

4

1 に答える 1

1

いくつかのアイデア:

  1. Code Firstを使用しないのはなぜですか? そうすれば、ビジネス ロジック (計算されたプロパティなど) をエンティティ クラスに正しく配置できます。

    public partial class SalesOrderLine
    {
        public int ID { get; set; }
        public int SalesOrderID { get; set; }
        public int ProductID { get; set; }
    
        private decimal _unitPrice;
        public decimal UnitPrice
        {
             get { return _unitPrice; }
             set
             {
                  if (value == _unitPrice) return;
                  _unitPrice = value;
                  CalculateExtendedPrice();
             }
        }
    
        private decimal _quantity;
        public decimal Quantity
        {
             get { return _quantity; }
             set
             {
                  if (value == _quantity) return;
                  _quantity= value;
                  CalculateExtendedPrice();
             }
        }
    
        public decimal ExtendedPrice { get; set; }
    
        public virtual Product Product { get; set; }
        public virtual SalesOrder SalesOrder { get; set; }
    
        private void CalculateExtendedPrice()
        {
            ExtendedPrice = UnitPrice * Quantity;
        }
    }
    
  2. Code First を使用できない場合は、エンティティを部分クラスにして (まだそうでない場合)、ビジネス ロジックを別のコード ファイルに入れます (ただし、クラス名は同じです)。この方法では、メイン コード ファイルは生成時に上書きされますが、セカンダリ コード ファイルは残ります。これは、生成されたコードを処理する通常の方法です。

于 2012-09-13T01:00:32.983 に答える