LINQ to Entitiesは、メソッド 'System.String Format(System.String、System.Object、System.Object)'を認識しません


88

私はこのlinqクエリを持っています:

private void GetReceivedInvoiceTasks(User user, List<Task> tasks)
{
    var areaIds = user.Areas.Select(x => x.AreaId).ToArray();

    var taskList = from i in _db.Invoices
                   join a in _db.Areas on i.AreaId equals a.AreaId
                   where i.Status == InvoiceStatuses.Received && areaIds.Contains(a.AreaId)
                   select new Task {
                       LinkText = string.Format(Invoice {0} has been received from {1}, i.InvoiceNumber, i.Organisation.Name),
                       Link = Views.Edit
                   };
}

ただし、問題があります。タスクを作成しようとしています。リンクテキストを「Hello」のような定数文字列に設定すると、新しいタスクごとに問題ありません。ただし、上記では、請求書のプロパティを使用してプロパティリンクテキストを作成しようとしています。

このエラーが発生します:

base {System.SystemException} = {"LINQ toEntitiesはメソッド 'System.String Format(System.String、System.Object、System.Object)'メソッドを認識せず、このメソッドをストア式に変換できません。" }

誰もが理由を知っていますか?誰かがそれを機能させるためにこれを行う別の方法を知っていますか?


はい、元々それを見逃していました
AnonyMouse 2012

回答:


148

Entity Frameworkは、に相当するものがないSQL側でプロジェクションを実行しようとしていますstring.FormatAsEnumerable()Linq toObjectsを使用してそのパーツの評価を強制するために使用します。

私があなたに与えた前の答えに基づいて、私はあなたのクエリを次のように再構築します:

int statusReceived = (int)InvoiceStatuses.Received;
var areaIds = user.Areas.Select(x=> x.AreaId).ToArray();

var taskList = (from i in _db.Invoices
               where i.Status == statusReceived && areaIds.Contains(i.AreaId)
               select i)
               .AsEnumerable()
               .Select( x => new Task()
               {
                  LinkText = string.Format("Invoice {0} has been received from {1}", x.InvoiceNumber, x.Organisation.Name),
                  Link = Views.Edit
                });

また、クエリで関連エンティティを使用していることもわかります(Organisation.NameInclude。クエリに適切なものを追加するか、後で使用するためにそれらのプロパティを具体的に具体化してください。

var taskList = (from i in _db.Invoices
               where i.Status == statusReceived && areaIds.Contains(i.AreaId)
               select new { i.InvoiceNumber, OrganisationName = i.Organisation.Name})
               .AsEnumerable()
               .Select( x => new Task()
               {
                  LinkText = string.Format("Invoice {0} has been received from {1}", x.InvoiceNumber, x.OrganisationName),
                  Link = Views.Edit
                });

式ツリーの変換のためにselect-new-taskの部分がサーバー側で発生しないという事実に加えて、そうすることは望ましくないことにも注意する必要があります。おそらく、タスクをクライアント側で作成する必要があります。したがって、クエリの分離とタスクの作成はさらに明確になる可能性があります。
Tormod 2012

3
また、必要なInvoiceNumberとOrganisation.Nameが含まれている匿名タイプを選択することをお勧めします。請求書エンティティが大きい場合、2つしか使用していない場合でも、selectiとそれに続くAsEnumerableはすべての列をプルバックします。
Devin 2012

1
@Devin:はい、同意します-実際、2番目のクエリの例が行っているのはまさにそれです。
BrokenGlass 2012

15

IQueryableから派生しますIEnumerable。主な類似点は、クエリを作成すると、その言語でデータベースエンジンに送信されることです。このとき、C#に(クライアント側ではなく)サーバー上のデータを処理するように指示するか、SQLに処理するように指示します。データ。

つまり、基本的に言うとIEnumerable.ToString()、C#はデータ収集を取得ToString()し、オブジェクトを呼び出します。しかし、IQueryable.ToString()C#はSQLにToString()オブジェクトを呼び出すように指示しますが、SQLにはそのようなメソッドはありません。

欠点は、C#でデータを処理する場合、C#がフィルターを適用する前に、調べているコレクション全体をメモリに構築する必要があることです。

これを行う最も効率的な方法は、IQueryable適用できるすべてのフィルターと同様にクエリを作成することです。

そして、それをメモリに構築し、C#でデータのフォーマットを作成します。

IQueryable<Customer> dataQuery = Customers.Where(c => c.ID < 100 && c.ZIP == 12345 && c.Name == "John Doe");

 var inMemCollection = dataQuery.AsEnumerable().Select(c => new
                                                  {
                                                     c.ID
                                                     c.Name,
                                                     c.ZIP,
                                                     c.DateRegisterred.ToString("dd,MMM,yyyy")
                                                   });

3

SQLはそれをどうするかを知りませんstring.Formatが、文字列の連結を実行できます。

次のコードを実行すると、目的のデータを取得する必要があります。

var taskList = from i in _db.Invoices
               join a in _db.Areas on i.AreaId equals a.AreaId
               where i.Status == InvoiceStatuses.Received && areaIds.Contains(a.AreaId)
               select new Task {
                   LinkText = "Invoice " + i.InvoiceNumber + "has been received from " + i.Organisation.Name),
                   Link = Views.Edit
               };

実際にクエリを実行すると、これは使用するよりもわずかに高速になるはずですAsEnumerable(少なくとも、あなたと同じ元のエラーが発生した後、私自身のコードで見つけたものです)。C#でもっと複雑なことをしている場合でも、使用する必要がAsEnumerableあります。


2
わからないのLINQを使用FORMATMESSAGE機能に適合させることができなかった理由をdocs.microsoft.com/en-us/sql/t-sql/functions/... それまではあなたが解決策は、(物質化を強制することなく)である
MemeDeveloper

2
データベースの構造と関連する列の数によっては、代わりにこの方法を使用する方AsEnumerable()がはるかに効率的です。避けてAsEnumerable()ToList()本当にすべての結果をメモリに入れたいと思うまで。
Chris Schaller
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.