web-dev-qa-db-ja.com

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

私はこの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 to Entitiesはメソッド 'System.String Format(System.String、System.Object、System.Object)'メソッドを認識せず、このメソッドはストア式に変換できません。」 }

誰もが理由を知っていますか?誰もがこれを機能させる代替方法を知っていますか?

76
AnonyMouse

Entity FrameworkはSQL側でプロジェクションを実行しようとしていますが、SQL側では string.Format に相当するものはありません。 AsEnumerable() を使用して、Linq to Objectsでその部分の評価を強制します。

ベース 前の答え 私はあなたにクエリを次のように再構築することを与えました:

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.Name)を使用していることを確認します。適切なIncludeをクエリに追加するか、後で使用するためにこれらのプロパティを具体的に具体化します。

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
                });
137
BrokenGlass

IQueryableIEnumerableから派生します。主な類似点は、クエリを作成すると、その言語でデータベースエンジンにポストされることです。クライアント側ではありません)またはデータを処理するように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")
                                                   });
15
Nikolay

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
d219