Obsługiwane są tylko inicjatory, elementy członkowskie jednostek i właściwości nawigacji jednostek

102

Otrzymuję ten wyjątek:

Podany typ członkowski „Płatny” nie jest obsługiwany w LINQ to Entities. Obsługiwane są tylko inicjatory, elementy członkowskie jednostek i właściwości nawigacji jednostek.

    public ActionResult Index()
    {
        var debts = storeDB.Orders
            .Where(o => o.Paid == false)
            .OrderByDescending(o => o.DateCreated);

        return View(debts);
    }

Klasa My Model

public partial class Order
{
    public bool Paid {
        get {
            return TotalPaid >= Total;
        }
    }

    public decimal TotalPaid {
        get {
            return Payments.Sum(p => p.Amount);
        }
    }

Płatności to Powiązana tabela zawierająca kwotę pola. Zapytanie działa, jeśli usunę klauzulę Where, pokazującą prawidłowe informacje o płatnościach, jakąkolwiek wskazówkę, co jest nie tak z kodem?

Rozwiązany jak sugerowana odpowiedź:

    public ActionResult Index()
    {
        var debts = storeDB.Orders
            .OrderByDescending(o => o.DateCreated)
            .ToList()
            .Where(o => o.Paid == false);

        return View(debts);
    }
Marc
źródło
15
Prosta odpowiedź: w zapytaniach linq-to-encje nie można używać niezamapowanych właściwości! Tylko odwzorowane właściwości są tłumaczone na SQL.
Ladislav Mrnka

Odpowiedzi:

114

Jednostka próbuje przekonwertować Twoją płatną właściwość na SQL i nie może, ponieważ nie jest ona częścią schematu tabeli.

Co możesz zrobić, to pozwolić Entity odpytać tabelę bez filtru Płatne, a następnie odfiltrować te, które nie zostały opłacone.

public ActionResult Index()
{
    var debts = storeDB.Orders
        //.Where(o => o.Paid == false)
        .OrderByDescending(o => o.DateCreated);

    debts = debts.Where(o => o.Paid == false);

    return View(debts);
}

To oczywiście oznaczałoby, że przenosisz wszystkie dane z powrotem na serwer sieciowy i filtrujesz je. Jeśli chcesz filtrować na serwerze bazy danych, możesz utworzyć kolumnę obliczeniową w tabeli lub użyć procedury składowanej.

Eugene S.
źródło
25

Musiałem tylko rozwiązać podobny problem. Powyższe rozwiązania wymagają przetwarzania w pamięci, co jest złą praktyką (leniwe ładowanie).

Moim rozwiązaniem było napisanie helpera zwracającego predykat:

public static class Extensions
{
    public static Expression<Func<Order, bool>> IsPaid()
    {
        return order => order.Payments.Sum(p => p.Amount) >= order.Total;
    }
}

Możesz przepisać swoją instrukcję linq jako:

var debts = storeDB.Orders
                    .Where(Extensions.IsPaid())
                    .OrderByDescending(o => o.DateCreated);

Jest to przydatne, gdy chcesz ponownie użyć logiki obliczeniowej (DRY). Wadą jest to, że logika nie znajduje się w modelu domeny.

Koen Luyten
źródło
1
Istnieje wiele bibliotek, które próbują uczynić to podejście bardziej „wbudowanym”, patrz: stackoverflow.com/a/27383641/470183 . Linq-to-entity jest ograniczone do wyrażeń używających „funkcji kanonicznych” - które można przekształcić w SQL. C # 6 wprowadził „Funkcje z treścią wyrażeń”, ale nie są to prawdziwe lambdy (patrz: stackoverflow.com/a/28411444/470183 ). Mimo to dobrze byłoby mieć to w ramach, stąd WIBNI data.uservoice.com/forums/…
James Close
1
Dziękuję za ten prosty i zwięzły przykład Expression<Func<xx,yy>>. Wcześniej to rozumiałem, ale teraz wygląda to na oczywiste.
AlexB
17

Ten problem może również pochodzić z [NotMapped]właściwości, która ma taką samą nazwę w modelu bazy danych i modelu widoku.

AutoMapper próbuje wybrać go z bazy danych podczas projekcji; a właściwość NotMapped oczywiście nie istnieje w bazie danych.

Rozwiązaniem jest Ignorewłaściwość w konfiguracji AutoMapper podczas mapowania z modelu bazy danych do modelu widoku.

  1. Poszukaj [NotMapped]właściwości o nazwie Foow swoim modelu bazy danych.
  2. Poszukaj właściwości o tej samej nazwie Foow swoim modelu widoku.
  3. Jeśli tak jest, zmień konfigurację AutoMapper. Dodaj.ForMember(a => a.Foo, b => b.Ignore());
Pęto
źródło
Dang AutoMapper Projection też mnie złapał, dzięki za odpowiedź!
Chase Florell
15

Linq konwertuje instrukcje na instrukcje SQL i wykonuje je w bazie danych.

Teraz ta konwersja występuje tylko dla elementów członkowskich jednostek, inicjatorów i właściwości nawigacji jednostek. Aby uzyskać porównanie funkcji lub właściwości, musimy najpierw przekonwertować je na listę w pamięci, a następnie zastosować funkcję w celu pobrania danych.

Dlatego w całości

var debts = storeDB.Orders.toList()
        .Where(o => o.Paid == false)
        .OrderByDescending(o => o.DateCreated);
T Gupta
źródło
21
Sugerowałbym, że poproszenie kogoś o zrobienie toList () na zlecenie jest niebezpieczne, ponieważ oznaczałoby pobranie całej listy
elgrego
Jest to dla mnie dobre, ponieważ moja problematyczna właściwość znajduje się w funkcji Sum Linq, a nie w klauzuli Where. Więc nie otrzymuję niepotrzebnych danych, a na odzyskanych danych wykonuję funkcję Linq Sum, która działa na liście. Dziękuję Ci! To, co na początku może wyglądać źle, może być bardzo pomocne w pewnych sytuacjach!
Dov Miller
11

Innym prawdopodobnym powodem jest to IEnumerable, że zamiast tego używasz dla swojej nieruchomościICollection

Więc zamiast:

public class This
{
    public long Id { get; set; }
    //...
    public virtual IEnumerable<That> Thats { get; set; }
}

Zrób to:

public class This
{
    public long Id { get; set; }
    //...
    public virtual ICollection<That> Thats { get; set; }
}

A ty jesteś hunky dory ... głupio tracić 2 godziny.

Serj Sagan
źródło
2

Ta sytuacja może się również zdarzyć, jeśli używasz nieobsługiwanych przez typy EntityFramework , takie jak unsigned int.

To był mój przypadek takiego błędu.

Sprawdź dalsze informacje na temat obsługiwanych typów: https://msdn.microsoft.com/en-us/library/ee382832(v=vs.100).aspx

Istnieje pewne obejście takich sytuacji, wyjaśnione przez GFoley83: Jak używać typów int / long bez znaku w Entity Framework?

Ony
źródło
Ten link zaoszczędził sporo czasu! Dziękuję bardzo!
Vladimir Semashkin
0

Napotkałem ten problem, ponieważ miałem zmienną składową z tylko get without setwłaściwością

to znaczy jego auto calculatedi not storedjako kolumna wthe table

dlatego jest not existwtable schema

tak make sure, że każda zmienna członek not auto calculateddo i właściwościhavegettersetter

Basheer AL-MOMANI
źródło
-1

Twój edmx i model kontekstu mają pewną inną właściwość, która jest nowo dodana do bazy danych.

Zaktualizuj EDMX, odśwież go poprawnie Utwórz projekt i uruchom ponownie.

To rozwiąże Twój problem.

Pozdrawiam, Ganesh Nikam

Ganesh Nikam
źródło