12

以下のコードを検討してください。

StockcheckJobs = 
     (from job in (from stockcheckItem in MDC.StockcheckItems
                   where distinctJobs.Contains(stockcheckItem.JobId)
                   group stockcheckItem by new { stockcheckItem.JobId, stockcheckItem.JobData.EngineerId } into jobs
                   select jobs).ToList()
      let date = MJM.GetOrCreateJobData(job.Key.JobId).CompletedJob.Value
      orderby date descending 
      select new StockcheckJobsModel.StockcheckJob()
      {
          JobId = job.Key.JobId,
          Date = date,
          Engineer = (EngineerModel)job.Key.EngineerId,
          MatchingLines = job.Count(sti => sti.Quantity == sti.ExpectedQuantity),
          DifferingLines = job.Count(sti => sti.Quantity != sti.ExpectedQuantity)
      }).ToList()

メソッドをSQLに変換できないToList()ため、途中にaがあります。GetOrCreateJobData

その結果、これを行うには、クエリの最初の部分を角かっこで囲む必要があり、外側のクエリを使用して終了しました。

これを 2 つの変数に分割できることはわかっていますが、そうしたくありません (これもオブジェクト初期化子内にあります)。

ToListlinq クエリの途中で (またはそうでなければ linq-to-objects に到達する)必要がある場合に、読みやすさを向上させるために使用できる他の構文はありますか?


理想的な世界では、次のようなものが欲しいです(とにかく可能な限り近い):

StockcheckJobs =
     from stockcheckItem in MDC.StockcheckItems
     where distinctJobs.Contains(stockcheckItem.JobId)
     group stockcheckItem by new { stockcheckItem.JobId, stockcheckItem.JobData.EngineerId } into jobs
     MAGIC_DO_BELOW_AS_LINQ-TO-OBJECTS_KEYWORD_OR_SYNTAX
     let date = MJM.GetOrCreateJobData(jobs.Key.JobId).CompletedJob.Value
     orderby date descending 
     select new StockcheckJobsModel.StockcheckJob()
     {
         JobId = jobs.Key.JobId,
         Date = date,
         Engineer = new ThreeSixtyScheduling.Models.EngineerModel() { Number = jobs.Key.EngineerId },
         MatchingLines = jobs.Count(sti => sti.Quantity == sti.ExpectedQuantity),
         DifferingLines = jobs.Count(sti => sti.Quantity != sti.ExpectedQuantity)
     };
4

4 に答える 4

5

GetOrCreateJobDataSQL に変換できない という問題を修正できます。

指定したメソッド呼び出し式のカスタム クエリ トランスレーターを実装することで、LINQ-to-SQL がメソッドを解釈する方法を制御できます。この手順を説明し、関連リソースへのリンクが記載された優れた記事がhttp://www.codeproject.com/Articles/32968/QueryMap-Custom-translation-of-LINQ-expressionsにあります。

GetOrCreateJobDataまたは、式を使用して同じロジックを構築する拡張メソッドにメソッドをリファクタリングして、LINQ-to-SQL が自然に解釈できるようにすることもできます。メソッドの複雑さに応じて、これは私の最初の提案より多かれ少なかれ実現可能かもしれません。

于 2012-09-20T15:21:45.317 に答える
3

メソッド構文を使用すると状況がより明確になることがわかりましたが、それは個人的な好みです。それは確かにクエリの上半分をより良くしますがlet、メソッド構文では可能ですが、を使用することはもう少し手間がかかります。

var result = stockcheckItem in MDC.StockcheckItems
    .Where(item => distinctJobs.Contains(item.JobId))
    .GroupBy(item => new { item.JobId, item.JobData.EngineerId })
    .AsEnumerable() //switch from Linq-to-sql to Linq-to-objects
    .Select(job => new StockcheckJobsModel.StockcheckJob()
    {
        JobId = job.Key.JobId,
        Date = MJM.GetOrCreateJobData(job.Key.JobId).CompletedJob.Value,
        Engineer = (EngineerModel)job.Key.EngineerId,
        MatchingLines = job.Count(sti => sti.Quantity == sti.ExpectedQuantity),
        DifferingLines = job.Count(sti => sti.Quantity != sti.ExpectedQuantity)
    })
    .Orderby(item => item.Date)
    .ToList()
于 2012-09-20T15:33:39.733 に答える
3

私は質問で2つのポイントを提起します:

  1. ここに追加の変数を導入しても読みやすさの問題はないと思います。実際、 「ローカルで実行する」コードをデータベースで実行するコードから分離するので、読みやすくなると思います。
  2. 単純に LINQ-To-Objects に切り替えるにAsEnumerableは、ToList.

そうは言っても、クエリ式全体で中間の AsEnumerable() / ToList() なしで、クエリランドにとどまる方法は次のとおりです。C#コンパイラをだまして、BCLではなくカスタム拡張メソッドを使用させることによって。これが可能なのは、C# が (BCL と結合するのではなく) 「パターンベース」のアプローチを使用して、クエリ式をメソッド呼び出しとラムダに変換するためです。

次のような悪のクラスを宣言します。

public static class To
{
    public sealed class ToList { }

    public static readonly ToList List;

    // C# should target this method when you use "select To.List"
    // inside a query expression.
    public static List<T> Select<T>
        (this IEnumerable<T> source, Func<T, ToList> projector)
    {
        return source.ToList();
    }
}

public static class As
{
    public sealed class AsEnumerable { }

    public static readonly AsEnumerable Enumerable;

    // C# should target this method when you use "select As.Enumerable"
    // inside a query expression.
    public static IEnumerable<T> Select<T>
        (this IEnumerable<T> source, Func<T, AsEnumerable> projector)
    {
        return source;
    }
}

そして、次のようなクエリを書くことができます:

List<int> list = from num in new[] { 41 }.AsQueryable()
                 select num + 1 into result
                 select To.List;

IEnumerable<int> seq = from num in new[] { 41 }.AsQueryable()
                       select num + 1 into result
                       select As.Enumerable into seqItem
                       select seqItem + 1; // Subsequent processing

あなたの場合、クエリは次のようになります。

StockcheckJobs =
     from stockcheckItem in MDC.StockcheckItems
     where distinctJobs.Contains(stockcheckItem.JobId)
     group stockcheckItem by new { stockcheckItem.JobId, stockcheckItem.JobData.EngineerId } into jobs
     select As.Enumerable into localJobs // MAGIC!
     let date = MJM.GetOrCreateJobData(localJobs.Key.JobId).CompletedJob.Value
     orderby date descending 
     select new StockcheckJobsModel.StockcheckJob()
     {
         JobId = localJobs.Key.JobId,
         Date = date,
         Engineer = new ThreeSixtyScheduling.Models.EngineerModel() { Number = localJobs.Key.EngineerId },
         MatchingLines = localJobs.Count(sti => sti.Quantity == sti.ExpectedQuantity),
         DifferingLines = localJobs.Count(sti => sti.Quantity != sti.ExpectedQuantity)
     };

しかし、私はこれを何らかの改善とは考えていません。むしろ、言語機能のかなりの悪用です。

于 2012-09-20T15:46:06.120 に答える
0

1 つのオプションは、すべての SQL 互換作業を前もって匿名型に行うことです。

var jobs = 
     (from job in (from stockcheckItem in MDC.StockcheckItems
        where distinctJobs.Contains(stockcheckItem.JobId)
        group stockcheckItem by new 
             { stockcheckItem.JobId, stockcheckItem.JobData.EngineerId } 
         into jobs
        select new 
             {
                JobId = job.Key.JobId,
                Engineer = (EngineerModel)job.Key.EngineerId,
                MatchingLines = 
                    job.Count(sti => sti.Quantity == sti.ExpectedQuantity),
                DifferingLines = 
                    job.Count(sti => sti.Quantity != sti.ExpectedQuantity)
             }
      ).AsEnumerable()

StockcheckJobs = jobs.Select(j => new StockcheckJobsModel.StockcheckJob
    {
         JobId = j.JobId,
         Date = MJM.GetOrCreateJobData(j.JobId).CompletedJob.Value,
         Engineer = j.EngineerId,
         MatchingLines = j.MatchingLines,
         DifferingLines = j.DifferingLines
    }).OrderBy(j => j.Date).ToList();

明らかにテストされていませんが、アイデアはわかります。

于 2012-09-20T15:26:01.317 に答える