2012-04-09 13 views
59

mam tej kwerendy LINQ:LINQ do podmiotów nie rozpoznaje metoda 'system.string Format (System.String, System.Object, System.Object)'

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

Ma problemy chociaż. Próbuję tworzyć zadania. Dla każdego nowego zadania, gdy ustawiam tekst linku na ciąg stały taki jak "Cześć", wszystko jest w porządku. Jednak powyżej staram się zbudować linktekst nieruchomości za pomocą właściwości faktury.

otrzymuję ten błąd:

base {System.SystemException} = {"LINQ to Entities does not recognize the method 'System.String Format(System.String, System.Object, System.Object)' method, and this method cannot be translated into a store expression."}

ktoś wie dlaczego? Czy ktoś wie, jak to zrobić, aby to zadziałało?

+0

Tak, brakowało że z pierwotnie – AnonyMouse

+0

możliwy duplikat [LINQ podmiotom nie rozpoznaje metody „system.string toString()” metoda] (http://stackoverflow.com/questions/4121863/linq- do-encji-nie-rozpoznaje-metody-system-string-tostring-method) –

Odpowiedz

115

Entity Framework próbuje wykonać projekcję po stronie SQL, gdzie nie ma odpowiednika string.Format. Użyj AsEnumerable(), aby wymusić ocenę tej części za pomocą Linq to Objects.

oparciu on the previous answer dałem wam chciałbym zrestrukturyzować zapytanie tak:

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

Również widzę korzystania z podmiotami powiązanymi w zapytaniu (Organisation.Name) upewnij się dodać odpowiednią Include z zapytaniem, czy specjalnie urzeczywistnić te właściwości do późniejszego wykorzystania, a mianowicie:

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

Oprócz faktu, że część select-new-task nie może się stać po stronie serwera z powodu translacji drzewa wyrażeń, powinna być również zauważyłem, że nie jest to pożądane. Prawdopodobnie chcesz, aby zadania zostały utworzone po stronie klienta. Dlatego rozdzielenie zapytania i tworzenie zadań może być jeszcze bardziej wyraźne. – Tormod

+3

Polecam również wybór anonimowego typu, który ma tylko potrzebny numer faktury i organizację. Nazwa w nim. Jeśli encja faktur jest duża, wybierz i wraz z kolejnym AsEnumerable wycofa każdą kolumnę, nawet jeśli używasz tylko dwóch. – Devin

+1

@Devin: Tak, zgadzam się - w rzeczywistości jest to dokładnie to, co robi drugi przykład zapytania. – BrokenGlass

14

IQueriable wywodzi się z IEnumerable, głównym podobieństwo jest to, że podczas dokonywania zapytanie zostanie wysłane do silnika bazy danych w to lang W tym cienkim momencie mówi się C#, aby poradził sobie z danymi na serwerze (nie po stronie klienta) lub nakazał SQLowi obsługę danych.

Tak więc w zasadzie, gdy mówisz IEnumerable.ToString(), C# pobiera gromadzenie danych i wywołuje ToString() na obiekcie. Ale kiedy mówisz IQueriable.ToString() C# mówi SQL, aby wywołać ToString() na obiekcie, ale nie ma takiej metody w SQL.

Wadą jest to, że podczas obsługi danych w języku C# cała kolekcja, którą szukasz, musi zostać zbudowana w pamięci, zanim C# zastosuje filtry.

Najbardziej skutecznym sposobem na zrobienie tego jest wysłanie zapytania jako IQueriable z wszystkimi filtrami, które można zastosować.

A następnie zbuduj go w pamięci i wykonaj formatowanie danych w języku 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") 
                }); 
Powiązane problemy