web-dev-qa-db-de.com

LINQ to Entities erkennt die Methode 'System.String-Format (System.String, System.Object, System.Object)' nicht

Ich habe diese linq-Abfrage:

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
                   };
}

Es hat jedoch Probleme. Ich versuche, Aufgaben zu erstellen. Für jede neue Aufgabe ist es in Ordnung, wenn ich den Linktext auf eine konstante Zeichenfolge wie "Hallo" setze. Aber oben versuche ich, den Eigenschaftslinktext mit den Eigenschaften der Rechnung zu erstellen.

Ich erhalte diesen Fehler:

base {System.SystemException} = {"LINQ to Entities erkennt die Methode 'System.String Format (System.String, System.Object, System.Object)' nicht. Diese Methode kann nicht in einen Speicherausdruck übersetzt werden." }

Weiß jemand warum? Kennt jemand einen alternativen Weg, um es funktionieren zu lassen?

71
AnonyMouse

Entity Framework versucht, Ihre Projektion auf der SQL-Seite auszuführen, wenn es keine Entsprechung zu string.Format gibt. Verwenden Sie AsEnumerable() , um die Bewertung dieses Teils mit Linq für Objekte zu erzwingen.

Basierend auf auf der vorherigen Antwort Ich habe Ihnen gegeben, ich würde Ihre Anfrage folgendermaßen umstrukturieren:

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

Ich sehe auch, dass Sie verwandte Entitäten in der Abfrage verwenden (Organisation.Name). Stellen Sie sicher, dass Sie der Abfrage die richtige Include hinzufügen oder diese Eigenschaften speziell für die spätere Verwendung materialisieren, d.

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

IQueryable leitet sich von IEnumerable ab. Die Hauptähnlichkeit besteht darin, dass, wenn Sie Ihre Abfrage in der Sprache der Datenbank-Engine veröffentlichen, der dünne Moment der Moment ist, in dem Sie C # mitteilen, dass es die Daten auf dem Server (nicht clientseitig) oder SQL anweist mit Daten umgehen.

Wenn Sie also IEnumerable.ToString() sagen, ruft C # die Datenerfassung ab und ruft ToString() für das Objekt auf. Wenn Sie jedoch IQueryable.ToString() sagen, sagt C # SQL, dass ToString() für das Objekt aufgerufen werden soll. In SQL gibt es jedoch keine solche Methode.

Der Nachteil ist, dass beim Umgang mit Daten in C # die gesamte Sammlung, die Sie durchsehen, im Speicher aufgebaut werden muss, bevor C # die Filter anwendet.

Am effizientesten ist es, die Abfrage als IQueryable mit allen Filtern zu definieren, die Sie anwenden können.

Und dann bauen Sie es im Speicher auf und nehmen Sie die Datenformatierung in C # vor. 

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

Während SQL nicht weiß, was mit einem string.Format zu tun ist, kann es eine String-Verkettung durchführen.

Wenn Sie den folgenden Code ausführen, sollten Sie die Daten erhalten, nach denen Sie suchen. 

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
               };

Sobald Sie die Abfrage tatsächlich ausführen, sollte dies geringfügig schneller sein als die Verwendung von AsEnumerable (zumindest habe ich dies in meinem eigenen Code gefunden, nachdem ich den gleichen ursprünglichen Fehler wie Sie hatte). Wenn Sie etwas komplexeres mit C # machen, müssen Sie dennoch AsEnumerable verwenden.

0
d219