2012-03-12 9 views
5

私はいくつかの集計データを取得するために多くのサブクエリを持つエンティティクエリに、次のLINQを持っているとSQL /エンティティクエリに:リファクタリングLINQ多くのサブクエリ

var systems = from s in db.Systems 
       orderby s.Name 
       select new SystemSummary 
       { 
        Id = s.Id, 
        Code = s.Code, 
        Name = s.Name, 
        LastException = (
         from a in s.Applications 
         from e in a.Summaries 
         select e.CreationDate 
       ).Max(), 
        TodaysExceptions = (
         from a in s.Applications 
         from e in a.Summaries 
         where e.CreationDate >= today && e.CreationDate < tomorrow 
         select e 
       ).Count(), 
        /* SNIP - 10-15 more subqueries */        
       }; 

私はサブクエリの2が含まれるように問合せを短縮しかし、それらのうちの約10-15がさらに存在する可能性があります。コードをクリーンアップするためにクエリをリファクタリングできる方法はありますか?私はパフォーマンスの向上を望んでいない。私はちょうどサブクエリを別のメソッドに置いてコードをクリーンアップし、それがデータベースへの一回の呼び出しであることを確認します。これは可能ですか?

+0

クリーンなコードが必要な場合は、データベースにストアドプロシージャを作成することをお勧めします。 – Mathieu

+0

@マチュー唯一の方法ですか? – Dismissile

答えて

2

私は(元のクエリでletキーワードを使用して)、このような何かによってその長さを最小限に提供することができます実際には

subQuery.Count(e=>e.CreationDate >= today && e.CreationDate < tomorrow); 

subQuery.max(e=>e.CreationDate); 

var subQuery = from a in s.Applications 
        from e in a.Summaries 
        select e; 

また、あなたのようないくつかのリファクタリングを持つことができますドット表記法を使用して、where節の代わりに関連する関数にクエリを移動してください。

とクエリでsubQueryを使用します。

  from s in db.Systems 
      orderby s.Name 
      let subQuery = from a in s.Applications 
        from e in a.Summaries 
        select e 
      select new SystemSummary 
      { 
       Id = s.Id, 
       Code = s.Code, 
       Name = s.Name, 
       LastException = subQuery.max(e=>e.CreationDate), 
       TodaysExceptions = subQuery.Count(e=>e.CreationDate >= today 
              && e.CreationDate < tomorrow), 
       /* SNIP - 10-15 more subqueries */        
      }; 

これはまだデシベルへの単一の呼び出しです。

+0

@Dismissile、私は 's'を逃しました。私は答えを編集しました。あなたのクエリでこの方法をシミュレートするために' let'を使うことができます。 –

+0

私はこのアプローチが好きです。 – Dismissile

+0

このヘルプがうまくいけば、あなたのコメントを参照してください:) –

0

実際には、複数の方法にクエリを分ける際に問題はありません。しかし、いくつかの条件があります。

クエリがIEumerableであることを確認してください。これはデフォルトです。

IEnumerableは、クエリが変数に格納されているが実行されていないことを保証します。 コンパイラは実行時にクエリを最適化します。

クイックや汚れexemple:

private MyContext context = new MyContext() 
private IEnumerable<User> getUser(Guid userID) 
{ 
    return context.User.Where(c => c.ID == userID); 
} 

private void evaluateUser() 
{ 
    bool isUserActive getUser().Any(c => c.IsActive) 
} 

クエリは二つの方法であることがわかります。 IEnumerableはクエリではなく結果を格納するため、DBへの呼び出しは1つだけです。クエリは、必要なときにのみ実行されます。

+2

IEnumerableではなくIQueryableを意味すると思います。IEnumerableはクエリをメモリ内オブジェクトに変換します。 –

0

letキーワードを使用してクエリのローカル変数を作成することをお勧めします(これは最終的にサブクエリになります)。例:あなたができる

var systems = from s in db.Systems 
       orderby s.Name 
       let lastException = (from a in s.Applications from e in a.Summaries select e.CreationDate).Max() 
       ... 

別のオプションは、おそらく右のバット、各種団体からのサブクエリを作成し、それらの要素で動作しています。

var systems = from s in db.Systems 
       orderby s.Name 
       from summaries in 
        (from ta in s.Applications 
        from te in ta.Summaries 
        ... 
        select { APPS = ta, SUMMS = te ,/*anything else you want*/ }) 
       let lastExpire = (from summaries select SUMMS.CreationDate).Max() 

地獄、あなたも、ちょうど第二の例では外に出して残し、ちょうどあなたの最後の選択でsummariesエンティティを使用することができます。値を重複しないようにするにはちょっと試してみる必要があるかもしれませんが、少なくともこの方法では毎回サブクエリを書き換えるのではなく、summariesに対してストレートセレクトを行うだけです。