2009-09-02 4 views
1

L2Eを使用してデータモデルのオブジェクトにビヘイビアを追加する場合の推奨アプローチは何ですか?あなたが直接、データモデルのインスタンスを使用してLINQ to Entitiesモデルに振る舞いを追加する

using (var dbh = new ffEntities()) 
    { 
     var query = from feed in dbh.feeds select 
        new FFFeed(feed.name, new Uri(feed.uri), feed.refresh); 
     return query.ToList(); 
    } 
    //Later in a separate place, not even in the same class 
    foreach (FFeed feed in feedList) { feed.doX(); } 
  • を必要とし、それらのインスタンスのIEnumerableを上で動作する方法を持っているデータだけで必要な動作を実装ラッパークラスを持つ

    • using (var dbh = new ffEntities()) 
          { 
           var query = from feed in dbh.feeds select feed; 
           return query.ToList(); 
          } 
          //Later in a separate place, not even in the same class 
          foreach (feeds feed in feedList) { doX(feed); } 
      
    • データモデルクラスの拡張メソッドを使用することで、ラッパーに追加のメソッドを持たせることになります。 1が最高である

      public static class dataModelExtensions { 
           public static void doX(this feeds source) { 
            //do X 
           } 
          } 
          //Later in a separate place, not even in the same class 
          foreach (feeds feed in feedList) { feed.doX(); } 
      

    ?私はそれがきれいで、CRUD施設に干渉しないように最後のアプローチを好む傾向があります(私はちょうどそれを挿入/更新/直接削除する必要はありません、戻ってものをラップする必要はありません)が、見たことがない。

    4つ目のアプローチはありますか?私は、特にLINQ to Entitiesに関してLINQの哲学を少しは理解していません。

  • 答えて

    1

    Entityクラスは私が知る限り部分クラスですから、partialキーワードを使って直接拡張する別のファイルを追加できます。

    これ以外の場合は、通常、私のViewModel(MVVMでWPFを使用しています)というラッパークラスがあります。私はまた、私はViewModelに特定のクエリフィルタを追加するために使用する流暢なインターフェイスを持ついくつかの一般的なヘルパークラスを持っています。

    1

    エンティティタイプに振る舞いを入れるのは間違いだと思います。

    Entity FrameworkはEntity Data Modelをベースにしており、アーキテクトの1人が「.NETのオブジェクトデータモデルに非常に近い」と説明しています。言い換えれば、エンティティ・モデルは、リレーショナル・データをオブジェクト・スペースにマップするように設計されていますが、メソッドで拡張するべきではありません。ビジネスタイプのメソッドを保存します。

    他のORMとは異なり、ブラックボックスからどのようなオブジェクトタイプが出てきても構いません。エンティティタイプとは異なる形になっていても、LINQを使用してほぼすべてのタイプに投影できます。ビジネスコード、データ転送、またはプレゼンテーションモデルではなく、マッピングにのみエンティティタイプを使用します。

    エンティティタイプは、コード生成時に部分的に宣言されます。これにより、開発者の中にはビジネスタイプに拡張しようとする人もいます。これは間違いです。実際、エンティティタイプを拡張することはめったに良い考えではありません。エンティティモデル内で作成されたプロパティは、LINQ to Entitiesでクエリできます。部分クラスに追加するプロパティまたはメソッドは、クエリに含めることはできません。

    は、ビジネス方法のこれらの例を考えてみましょう:

    public Decimal CalculateEarnings(Guid id) 
    { 
        var timeRecord = (from tr in Context.TimeRecords 
             .Include(“Employee.Person”) 
             .Include(“Job.Steps”) 
             .Include(“TheWorld.And.ItsDog”) 
             where tr.Id = id 
             select tr).First(); 
        // Calculate has deep knowledge of entity model 
        return EarningsHelpers.Calculate(timeRecord); 
    } 
    

    この方法で間違っているのですか?生成されたSQLは、Entity Frameworkに、Calculateメソッドで必要とされる少数のプロパティを取得するためにオブジェクト全体のインスタンスを実現するように依頼しているため、厄介な複雑さを抱くことになります。コードも脆弱です。モデルを変更するだけで、(Include呼び出しを使用して)熱心な読み込みが中断されるだけでなく、Calculateメソッドも中断されます。

    単一責任原則では、クラスに変更する理由が1つだけある必要があります。画面に表示された例では、EarningsHelpersタイプは実際に収益を計算し、エンティティモデルの変更を最新の状態に保つ役割を担います。最初の責任は正しいと思われ、第二の責任は正しいとは言えません。私たちがそれを修正できるかどうかを見てみましょう。次の例で

    public Decimal CalculateEarnings(Guid id) 
    { 
        var timeData = from tr in Context.TimeRecords 
            where tr.Id = id 
            select new EarningsCalculationContext 
            { 
             Salary = tr.Employee.Salary, 
             StepRates = from s in tr.Job.Steps 
                select s.Rate, 
             TotalHours = tr.Stop – tr.Start 
            }.First(); 
        // Calculate has no knowledge of entity model 
        return EarningsHelpers.Calculate(timeData); 
    } 
    

    、Iは、計算方法の引数をロールアップ型に計算方法が必要とする情報、およびプロジェクト情報のビットのみを取り出すためにLINQクエリを書き換えています。メソッドに引数を渡すだけの新しい型を書くことが多すぎるようであれば、私は匿名型に射影し、Salary、StepRates、およびTotalHoursを個々の引数として渡すこともできました。しかしどちらの方法でも、EarningsHelpersのエンティティモデルへの依存を修正し、無料のボーナスとして、より効率的なSQLを取得しました。

    TimeRecordのJobプロパティがnullの場合、このコードを見て、何が起きるのだろうかと思います。 null参照例外が発生しないでしょうか?

    いいえ、私はしません。このコードはコンパイルされず、ILとして実行されません。それはSQLに変換されます。 LINQ to Entitiesはnull参照を結合します。画面に表示されているクエリの例では、StepRatesはJobがnullの場合はnullを返します。余分なデータベースクエリを除いて、これを遅延読み込みと同じと考えることができます。コードには、「仕事がある場合は、そのステップから料金を読み込んでください」と記載されています。

    このようなアーキテクチャの利点は、Webアセンブリの単体テストを非常に簡単にすることです。単体テストはデータベースにアクセスすべきではなく、一般的に言えば(データベースにアクセスするテストはユニットテストではなく統合テストです)。実際にEntity Frameworkに行くのではなく、オブジェクトの配列をQueryablesとして返すモックリポジトリを書くのは簡単です。

    +0

    突出部分がありませんでした。すばらしい答えがありました。私はまだ私が哲学を得るかどうかはわかりませんが。各ビジネスクラスは、必要なデータをすべてロードする代わりに、直接L2E経由で必要なデータを取得し、それを渡し、古いORMが奨励しているアプローチを保存します。 –

    +0

    いいえ、ビジネスクラスがデータを直接取得する必要はないと思います。代わりに、ビジネスクラス(EarningsHelpersとEarningsCalculationContext、私の簡単な例では)は、永続性を無視する必要があります。必要なデータを照会し、それをビジネスタイプに投影し、それをビジネスメソッドに渡すサービスまたはリポジトリレイヤーがあります。はい、あなたは正しいです、私は長い間、メモリ内の多くの異なるメソッドのためにたくさんのデータを保持するという考えが嫌いです。私は共有状態がスケーラビリティの敵であると感じています。 –

    関連する問題