it-swarm.com.de

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

Es gibt einen Entitätstyp namens Produkt, der vom Entitäts-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 löst den folgenden Fehler aus:

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

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

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

Wie kann ich einen benutzerdefinierten Auswahlbereich vorbereiten?

362
Ghooti Farangi

Sie können (und sollten nicht in der Lage sein), auf eine zugeordnete Entität zu projizieren. Sie können jedoch auf einen anonymen Typ oder auf ein DTO projizieren:

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

Und Ihre Methode gibt eine Liste von DTOs zurück.

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

Sie können in einen anonymen Typ projizieren und von diesem in einen 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 });
}

Edit: Ich werde etwas genauer sein, da diese Frage viel Aufmerksamkeit auf sich gezogen hat.

Sie können nicht direkt in den Modelltyp projizieren (EF-Einschränkung), daher führt kein Weg daran vorbei. Die einzige Möglichkeit besteht darin, in einen anonymen Typ (1. Iteration) und dann in einen Modelltyp (2. Iteration) zu projizieren.

Beachten Sie außerdem, dass beim teilweisen Laden von Entitäten auf diese Weise diese nicht aktualisiert werden können. Daher sollten sie so wie sie sind nicht verbunden bleiben.

Ich habe nie vollständig verstanden, warum dies nicht möglich ist, und die Antworten auf diesen Thread sprechen nicht mit starken Gründen dagegen (meistens handelt es sich um teilweise geladene Daten). Es ist richtig, dass eine teilweise geladene Entität nicht aktualisiert werden kann, diese Entität jedoch getrennt wird, sodass versehentliche Versuche, sie zu speichern, nicht möglich sind.

Betrachten Sie die oben verwendete Methode: Als Ergebnis haben wir immer noch eine teilweise geladene Modellentität. Diese Entität ist abgetrennt.

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 losgelöster Entitäten führen, sodass keine zwei Iterationen erforderlich sind. Ein Compiler wäre schlau, wenn er sehen würde, dass AsNoTracking () verwendet wurde, was zu getrennten Entitäten führt, sodass wir dies möglicherweise tun können. Wenn jedoch AsNoTracking () weggelassen wurde, kann es dieselbe Ausnahme auslösen wie jetzt, um uns zu warnen, dass wir genau genug über das gewünschte Ergebnis informiert sein müssen.

259
Goran

Es gibt einen anderen Weg, auf dem ich Werke 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.

74
Tomasz Iniewicz

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 ist jedoch nur zu verwenden, wenn Sie mehrere Entitäten in einer einzigen Entität kombinieren möchten. Die oben beschriebene Funktionalität (einfache Zuordnung von Produkt zu Produkt) sieht folgendermaßen aus:

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;
}
36
Bojan Hrnkas

Ein anderer 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;
}
22
Soren

Sie können dies verwenden und es sollte funktionieren -> Sie müssen toList verwenden, bevor Sie die neue Liste mit select erstellen:

db.Products
    .where(x=>x.CategoryID == categoryID).ToList()
    .select(x=>new Product { Name = p.Name}).ToList(); 
3
Mohamed Adam

Sie können dies mithilfe von Data Transfer Objects (DTOs) lösen.

Dies ähnelt Ansichtsmodellen, in denen Sie die gewünschten Eigenschaften eingeben und sie manuell in Ihrem Controller oder mithilfe von Lösungen von Drittanbietern wie AutoMapper zuordnen können.

Mit DTO können Sie:

  • Daten serialisierbar machen (Json)
  • Zirkuläre Verweise loswerden
  • Reduzieren Sie den Netzwerkverkehr, indem Sie nicht benötigte Eigenschaften belassen (Ansichtsmodell).
  • Verwenden Sie die Objektabflachung

Ich habe das in diesem Jahr in der Schule gelernt und es ist ein sehr nützliches Werkzeug.

1
Jelman

Als Antwort auf die andere Frage, die als Duplikat markiert war ( siehe hier ), fand ich eine schnelle und einfache Lösung basierend auf der Antwort von Soren:

data.Tasks.AddRange(
    data.Task.AsEnumerable().Select(t => new Task{
        creator_id   = t.ID,
        start_date   = t.Incident.DateOpened,
        end_date     = t.Incident.DateCLosed,
        product_code = t.Incident.ProductCode
        // so on...
    })
);
data.SaveChanges();

Hinweis: Diese Lösung funktioniert nur, wenn Sie über eine Navigationseigenschaft (Fremdschlüssel) für die Task-Klasse (hier "Incident" genannt) verfügen. Wenn Sie das nicht haben, können Sie einfach eine der anderen bereitgestellten Lösungen mit "AsQueryable ()" verwenden.

1
JollyBrackets

füge nur AsEnumerable () hinzu:

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

wenn Sie Linq to Entity ausführen, können Sie das ClassType nicht mit new im select Abschluss der Abfrage only anonymous types are allowed (new without type) verwenden.

schauen Sie sich diesen Ausschnitt aus meinem Projekt an

//...
var dbQuery = context.Set<Letter>()
                .Include(letter => letter.LetterStatus)
                .Select(l => new {Title =l.Title,ID = l.ID, LastModificationDate = l.LastModificationDate, DateCreated = l.DateCreated,LetterStatus = new {ID = l.LetterStatusID.Value,NameInArabic = l.LetterStatus.NameInArabic,NameInEnglish = l.LetterStatus.NameInEnglish} })
                               ^^ without type__________________________________________________________________________________________________________^^ without type

von Ihnen haben den new keyword in Select Closure hinzugefügt, selbst wenn Sie den complex properties eingegeben haben, wird Ihnen dieser Fehler angezeigt

also remove das Schlüsselwort ClassTypes from new für Linq to Entity - Abfragen ,

weil es in eine SQL-Anweisung umgewandelt und auf SqlServer ausgeführt wird

also wann kann ich new with types für select Closure verwenden?

sie können es verwenden, wenn Sie es mit LINQ to Object (in memory collection) zu tun haben.

//opecations in tempList , LINQ to Entities; so we can not use class types in select only anonymous types are allowed
var tempList = dbQuery.Skip(10).Take(10).ToList();// this is list of <anonymous type> so we have to convert it so list of <letter>

//opecations in list , LINQ to Object; so we can use class types in select
list = tempList.Select(l => new Letter{ Title = l.Title, ID = l.ID, LastModificationDate = l.LastModificationDate, DateCreated = l.DateCreated, LetterStatus = new LetterStatus{ ID = l.LetterStatus.ID, NameInArabic = l.LetterStatus.NameInArabic, NameInEnglish = l.LetterStatus.NameInEnglish } }).ToList();
                                ^^^^^^ with type 

nachdem ich ToList auf Anfrage ausgeführt habe, wurde es zu in memory collection, sodass wir new ClassTypes in select verwenden können

0

Wenn Sie das Entity-Framework verwenden, versuchen Sie, die Eigenschaft aus DbContext zu entfernen, die Ihr komplexes Modell als Entität verwendet. Ich hatte das gleiche Problem beim Zuordnen mehrerer Modelle zu einem Ansichtsmodell mit dem Namen Entität

public DbSet<Entity> Entities { get; set; }

Das Entfernen des Eintrags aus DbContext hat meinen Fehler behoben.

0
vikas suhag

In vielen Fällen ist die Transformation nicht erforderlich. Überlegen Sie, warum Sie List stark verwenden möchten, und prüfen Sie, ob Sie die Daten nur benötigen, beispielsweise in einem Webdienst oder zum Anzeigen. Es spielt keine Rolle, um welchen Typ es sich handelt. Sie müssen nur wissen, wie man es liest, und prüfen, ob es mit den Eigenschaften identisch ist, die in dem von Ihnen definierten anonymen Typ definiert sind. Dies ist das Optimierungsszenario, da nicht alle Felder einer Entität benötigt werden und aus diesem Grund ein anonymer Typ vorhanden ist.

Ein einfacher Weg ist dies:

IEnumerable<object> list = dataContext.Table.Select(e => new { MyRequiredField = e.MyRequiredField}).AsEnumerable();
0
Sterling Diaz