Mam takie zapytanie 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
};
}
Ma jednak problemy. Próbuję tworzyć zadania. Dla każdego nowego zadania, kiedy ustawię tekst łącza na stały ciąg, taki jak „Hello”, wszystko jest w porządku. Jednak powyżej próbuję zbudować tekst linku właściwości przy użyciu właściwości faktury.
Otrzymuję ten błąd:
base {System.SystemException} = {"LINQ to Entities nie rozpoznaje metody 'System.String Format (System.String, System.Object, System.Object)' i tej metody nie można przetłumaczyć na wyrażenie magazynu." }
Czy ktoś wie, dlaczego? Czy ktoś zna alternatywny sposób, aby to zadziałało?
linq
entity-framework
linq-to-entities
AnonyMouse
źródło
źródło
Odpowiedzi:
Entity Framework próbuje wykonać projekcję po stronie SQL, gdzie nie ma odpowiednika
string.Format
. SłużyAsEnumerable()
do wymuszania oceny tej części za pomocą Linq to Objects.Na podstawie poprzedniej odpowiedzi, której ci udzieliłem, przeformułowałbym twoje zapytanie w następujący sposób:
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 });
Widzę również, że używasz powiązanych encji w zapytaniu (
Organisation.Name
), upewnij się, że dodajesz właściweInclude
do zapytania lub konkretnie zmaterializujesz te właściwości do późniejszego wykorzystania, tj .: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 });
źródło
IQueryable
wywodzi się zIEnumerable
, główne podobieństwo polega na tym, że kiedy tworzysz zapytanie, jest ono wysyłane do silnika bazy danych w swoim języku, w cienkim momencie mówisz C #, aby obsłużył dane na serwerze (nie po stronie klienta) lub aby SQL obsługiwał dane.Zasadniczo, gdy mówisz
IEnumerable.ToString()
, C # pobiera kolekcję danych i wywołujeToString()
obiekt. Ale kiedy mówisz, żeIQueryable.ToString()
C # mówi SQLowi, aby wywołałToString()
obiekt, ale nie ma takiej metody w SQL.Wadą jest to, że podczas obsługi danych w języku C # cała przeglądana kolekcja musi zostać zgromadzona w pamięci, zanim C # zastosuje filtry.
Najskuteczniejszym sposobem jest ustawienie zapytania jako
IQueryable
przypadku wszystkich filtrów, które można zastosować.Następnie zapisz go w pamięci i sformatuj dane w 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") });
źródło
Chociaż SQL nie wie, co zrobić z a
string.Format
, może wykonać konkatenację ciągów.Jeśli uruchomisz poniższy kod, powinieneś otrzymać dane, których szukasz.
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 };
Gdy faktycznie wykonasz zapytanie, powinno to być nieznacznie szybsze niż użycie
AsEnumerable
(przynajmniej to znalazłem w moim własnym kodzie po tym samym pierwotnym błędzie co ty). Jeśli robisz coś bardziej złożonego w C #, nadal będziesz musiał używaćAsEnumerable
.źródło
AsEnumerable()
może być znacznie bardziej wydajne. UnikajAsEnumerable()
iToList()
dopóki naprawdę nie zechcesz zapamiętać wszystkich wyników.