428 Stimmen

Die Entität kann nicht in einer LINQ to Entities-Abfrage erstellt werden

Es gibt einen Entitätstyp namens Product die vom Entity Framework generiert wird. Ich habe diese Abfrage geschrieben

public IQueryable<Product> GetProducts(int categoryID)
{
    return from p in db.Products
           where p.CategoryID== categoryID
           select new Product { Name = p.Name};
}

Der folgende Code führt zu folgendem Fehler:

"Die Entität oder der komplexe Typ Shop.Product kann nicht in einem LINQ to Entities-Abfrage erstellt werden"

var products = productRepository.GetProducts(1).Tolist();

Aber wenn ich die select p anstelle von select new Product { Name = p.Name}; es funktioniert richtig.

Wie kann ich einen benutzerdefinierten Auswahlbereich erstellen?

434voto

Yakimych Punkte 17286

Sie können nicht auf eine zugeordnete Entität projizieren (und sollten es auch nicht können). Sie können jedoch auf einen anonymen Typ oder auf eine DTO :

public class ProductDTO
{
    public string Name { get; set; }
    // Other field you may need from the Product entity
}

Und Ihre Methode wird eine Liste von DTOs zurückgeben.

public List<ProductDTO> GetProducts(int categoryID)
{
    return (from p in db.Products
            where p.CategoryID == categoryID
            select new ProductDTO { Name = p.Name }).ToList();
}

293voto

Goran Punkte 6068

Sie können in den anonymen Typ projizieren und von dort in den Modelltyp

public IEnumerable<Product> GetProducts(int categoryID)
{
    return (from p in Context.Set<Product>()
            where p.CategoryID == categoryID
            select new { Name = p.Name }).ToList()
           .Select(x => new Product { Name = x.Name });
}

Editar : Da diese Frage viel Aufmerksamkeit erregt hat, werde ich etwas genauer werden.

Sie können nicht direkt in den Modelltyp projizieren (EF-Beschränkung), daher gibt es keine Möglichkeit, dies zu umgehen. Die einzige Möglichkeit ist, in den anonymen Typ zu projizieren (1. Iteration) und dann in den Modelltyp (2. Iteration).

Bitte beachten Sie auch, dass Entitäten, die Sie auf diese Weise teilweise laden, nicht aktualisiert werden können, sondern losgelöst bleiben sollten.

Ich habe nie ganz verstanden, warum dies nicht möglich ist, und die Antworten in diesem Thread enthalten keine stichhaltigen Gründe, die dagegen sprechen (meist geht es um teilweise geladene Daten). Es ist richtig, dass in teilweise geladenen Zustand Entität kann nicht aktualisiert werden, aber dann, diese Entität würde abgetrennt werden, so versehentliche Versuche, sie zu speichern wäre nicht möglich.

Betrachten Sie die Methode, die ich oben verwendet habe: Wir haben immer noch eine teilweise geladene Modellentität als Ergebnis. Diese Entität ist losgelöst.

Betrachten Sie diesen (wünschenswerten) möglichen Code:

return (from p in Context.Set<Product>()
        where p.CategoryID == categoryID
        select new Product { Name = p.Name }).AsNoTracking().ToList();

Dies könnte auch zu einer Liste von abgetrennten Einheiten führen, so dass wir nicht zwei Iterationen durchführen müssten. Ein Compiler würde erkennen, dass AsNoTracking() verwendet wurde, was zu abgetrennten Entitäten führt, und könnte uns dies erlauben. Wenn jedoch AsNoTracking() weggelassen wurde, könnte er die gleiche Ausnahme wie jetzt auslösen, um uns zu warnen, dass wir das gewünschte Ergebnis genau genug angeben müssen.

89voto

Tomasz Iniewicz Punkte 4281

Es gibt noch eine andere Möglichkeit, die ich gefunden habe: Sie müssen eine Klasse erstellen, die von Ihrer Produktklasse abgeleitet ist, und diese verwenden. Zum Beispiel:

public class PseudoProduct : Product { }

public IQueryable<Product> GetProducts(int categoryID)
{
    return from p in db.Products
           where p.CategoryID== categoryID
           select new PseudoProduct() { Name = p.Name};
}

Ich bin nicht sicher, ob dies "erlaubt" ist, aber es funktioniert.

37voto

Bojan Hrnkas Punkte 1440

Hier ist eine Möglichkeit, dies zu tun, ohne eine zusätzliche Klasse zu deklarieren:

public List<Product> GetProducts(int categoryID)
{
    var query = from p in db.Products
            where p.CategoryID == categoryID
            select new { Name = p.Name };
    var products = query.ToList().Select(r => new Product
    {
        Name = r.Name;
    }).ToList();

    return products;
}

Dies sollte jedoch nur verwendet werden, wenn Sie mehrere Entitäten in einer einzigen Entität zusammenfassen möchten. Die obige Funktionalität (einfache Produkt-zu-Produkt-Zuordnung) wird wie folgt durchgeführt:

public List<Product> GetProducts(int categoryID)
{
    var query = from p in db.Products
            where p.CategoryID == categoryID
            select p;
    var products = query.ToList();

    return products;
}

23voto

Soren Punkte 5851

Ein weiterer einfacher Weg :)

public IQueryable<Product> GetProducts(int categoryID)
{
    var productList = db.Products
        .Where(p => p.CategoryID == categoryID)
        .Select(item => 
            new Product
            {
                Name = item.Name
            })
        .ToList()
        .AsQueryable(); // actually it's not useful after "ToList()" :D

    return productList;
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X