wake-up-neo.net

Der richtige Weg, um mehrere Datensätze mit LINQ to Entities in eine Tabelle einzufügen

Wie viele von uns habe ich eine einfache Schleife eingerichtet, um mehrere Datensätze aus einer Datenbank hinzuzufügen. Ein prototypisches Beispiel wäre etwa so:

Methode I:

// A list of product prices
List<int> prices = new List<int> { 1, 2, 3 };

NorthwindEntities NWEntities = new NorthwindEntities();

foreach (int price in prices)
{
   Product newProduct = new Product();
   newProduct.Price = price;
   NWEntities.Products.AddObject(newProduct);
}

NWEntities.SaveChanges();

Beim ersten Einrichten der Schleife schrieb ich jedoch intuitiv:

Methode II:

Product newProduct = new Product();

foreach (int price in prices)
{
   newProduct.Price = price;
   NWEntities.Products.Add(newProduct);
}

Nach einer kurzen Lektüre erwähnten einige Personen, dass bei Verwendung der Methode II nur ein Datensatz zur Tabelle hinzugefügt würde. Dies scheint widersinnig zu sein. Es ist die Add () - Funktion, die eine neue Einfügung lädt und, denke ich, ein Objekt nach jedem Aufruf mit den übergebenen Daten erstellt. Wenn ich mein Produktobjekt outside deklariere, scheint die Schleife Ressourcen besser zu nutzen, als die Nur der bei jedem Aufruf verbrauchte Overhead wäre die Neuzuweisung der Objektinstanzeigenschaft und nicht die Neukonstruktion der Objektinstanz selbst. 

Kann jemand bitte klären? Ich konnte keinen anderen Beitrag finden, der sich direkt mit dieser Frage beschäftigt. Wenn einer da draußen ist, weisen Sie bitte darauf hin. 

25
seebiscuit

Bewegen Sie einfach die Instantiierung des neuen Produkts in die Schleife. Ihr Code, wie er geschrieben wird, fügt mehrmals eine einzige Instanz hinzu, die nicht das ergibt, wonach Sie gesucht haben. Sie benötigen für jedes Produkt eine eigene Instanz Kontext und markiert es zum Einfügen.

foreach (int price in prices)
{
   Product newProduct = new Product();
   newProduct.Price = price;
   NWEntities.Products.Add(newProduct);
}

Um etwas genauer zu sehen, was passiert, bedenken Sie folgendes: 

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Try to reuse same Instance:");
        using (var ctx = new AdventureWorksEntities())
        {
            List<int> ids = new List<int> {1, 2, 3}; 
            Product p1 = new Product();
            Product reference = p1;
            Product p2;
            Console.WriteLine("Start Count: {0}", ctx.Products.Count());
            foreach (var id in ids)
            {
                p1.ProductID = id;
                p2 = ctx.Products.Add(p1);
                Console.WriteLine("p1 = p2 ? {0}", p1 == p2);
                Console.WriteLine("p2 = reference? {0}", p2 == reference);
                Console.WriteLine("State: {0}", ctx.Entry(p1).State);
                var changes = ctx.ChangeTracker.Entries<Product>();
                Console.WriteLine("Change Count: {0}", changes.Count());
            }
        }
        Console.WriteLine();
        Console.WriteLine("Distinct Instances:");
        using (var ctx = new AdventureWorksEntities())
        {
            List<int> ids = new List<int> { 1, 2, 3 };
            Product p2;
            foreach (var id in ids)
            {
                var p1 = new Product {ProductID = id};
                p2 = ctx.Products.Add(p1);
                Console.WriteLine("p1 = p2 ? {0}", p1 == p2);
                Console.WriteLine("State: {0}", ctx.Entry(p1).State);
                var changes = ctx.ChangeTracker.Entries<Product>();
                Console.WriteLine("Change Count: {0}", changes.Count());
            }
        }

        Console.ReadLine();
    }
}

In der ersten Schleife verwenden Sie dieselbe Produktinstanz erneut. Wenn Sie sie jedoch dem Kontext hinzufügen, verwenden Sie jedes Mal dieselbe Referenz. Sie können sehen, dass der Änderungszähler bei 1 bleibt, unabhängig davon, wie oft die Schleife ausgeführt wird. Natürlich würden nur die letzten Werte gespeichert, wenn Sie ctx.SaveChanges () aufrufen.

In der zweiten Version wird die Änderungsanzahl jedes Mal korrekt erhöht, und Sie würden SaveChanges aufrufen, um alle verschiedenen Entitäten zu speichern, wie Sie es erwarten würden.

23
terryt

+1 Für Terryt's Antwort. Sie müssen bei Methode eins oder etwas Ähnlichem bleiben.

In der Entity Framework 6-Version gibt es eine neue Methode zum Hinzufügen eines Datensatzes in einer einzelnen Anweisung. Dies ist die AddRange-Methode .

Ich möchte hinzufügen, dass ich die AddRange-Methode elegant finde, wenn Sie Entitäten hinzufügen möchten, die auf einer vorhandenen Liste (oder IEnumerable) basieren. 

In Ihrem Fall könnte es so aussehen:

NWEntities.Products.AddRange(
    Prices.Select(priceitem =>
    new Product{price = priceitem})
)

Semantisch sollte dies Ihrer Methode ähneln. 1. Ein Produktobjekt wird pro Preis in der Preisliste instanziiert. Es gibt jedoch einen Unterschied, der anonym durchgeführt wurde. Es gibt also keine explizit definierten Referenzvariablen, die auf das neue Objekt verweisen.

Wenn Leistung wichtig ist, können Sie mit dieser Frage einige weitere Informationen erhalten: Schnellster Einfügevorgang in Entity Framework

Hoffe, das gibt dir etwas Hilfe.

14
karstenols

Wir würden die Hilfe von loop nicht brauchen. Wir können dies durch linq tun. Wie im folgenden Code müssen Namen aus der nameList mit dem Bitfeld IsDeleted in die Employee-Tabelle aufgenommen werden.

db.Employee.AddRange(
   nameList.Select(name =>
      new Employee
      {
           Name = name,
           IsDeleted = false
      })
   );
1

Ich hatte ein ähnliches Problem. In meiner Ausgabe hatte ich diesen Code: 

        var cratelist = db.TruckContainerLoads.Where(x => x.TruckID == truckid).Select(x => x.ContainerID);
        if (!cratelist.Any())
        {
            return;
        }
        foreach (var crateid in cratelist) {
            TruckContainerLoad crInstance = new TruckContainerLoad();
            crInstance.ContainerID = crateid;
            try
            {
                db.TruckContainerLoads.Add(crInstance);
                db.SaveChanges();
            }
            catch
            {
                return;
            }
        }

Meine Anfrage hat nur den ersten Datensatz in meinem Foreach hinzugefügt. Das Problem war, dass ich meine db.SaveChanges () außerhalb der foreach-Schleife aufrufen musste, nachdem ich mehrere Datensätze hinzugefügt hatte. Für mich war die Antwort auf meine Frage tatsächlich in der Frage. Also, ich stimme der Frage zu. 

0
Patrick Knott