2013-07-22 9 views
9

In meinem letzten Projekt habe ich Entity Framework 5 Code First verwendet. Ich habe mein Projekt abgeschlossen, hatte aber während des Entwicklungsprozesses große Schmerzen.Hinzufügen und Aktualisieren von Entitäten mit Entity Framework

Ich habe versucht, meinen Schmerz zu erklären, unten:

ich in meiner Datenzugriffslogik-Schicht wie Produkt, Product, Ordnung, Company, Hersteller usw. mehrere Datenklassen hatte ... hat jede Klasse einige Verweise auf einige andere Klassen . Eine Produktinstanz hat beispielsweise eine ProductCategory-Eigenschaft.

Inside Add- und Update-Methoden meiner Data Access-Objektklassen Ich setze die Zustände jeder Eigenschaft (mit Ausnahme der primitiven Typen) im Kontext auf Unverändert oder Modifiziert. Nachfolgend finden Sie einige Teil einer Update-Methode von einigen dao Klasse:

context.Entry(entity).State = System.Data.EntityState.Modified; 
if (entity.Category != null) 
    context.Entry(entity.Category).State = System.Data.EntityState.Unchanged; 

if (entity.Manufacturer != null) 
    context.Entry(entity.Manufacturer).State = System.Data.EntityState.Unchanged; 

foreach (var specificationDefinition in entity.SpecificationDefinitions) 
{ 
    context.Entry(specificationDefinition).State = System.Data.EntityState.Unchanged; 
    foreach (var specificationValue in specificationDefinition.Values) 
    { 
     context.Entry(specificationValue).State = System.Data.EntityState.Unchanged; 
    } 
} 

Dieser Code wie das geht. Einige meiner Add- oder Update-Methoden sind etwa 30 Zeilen Code. Ich denke, dass ich etwas falsch mache, das Hinzufügen oder Aktualisieren einer Entität sollte nicht so viel Schmerz sein, aber wenn ich die Zustände der Objekte nicht setze, erhalte ich entweder eine Ausnahme oder doppelte Einträge in der Datenbank. Muss ich wirklich den Status jeder Eigenschaft festlegen, die der Datenbank zugeordnet ist?

+0

Können Sie den Code hinzufügen, in dem Sie die Daten erstellen? Wenn Ihre Referenzen in Ihren Klassen korrekt sind, sollte EF nur alle Instanzen einmal erstellen. – Nullius

+0

Ja, es erstellt alle Instanzen gleichzeitig. Das ist das eigentliche Problem. Lassen Sie mich das Problem noch einmal erklären. Angenommen, ich habe eine Instanz von Product, in der sich eine ProductType-Instanz befindet. Angenommen, die Produktinstanz wurde bereits in der Vergangenheit beibehalten. Wenn ich versuche, diese Product-Instanz zu aktualisieren, erstellt EF eine doppelte ProductType-Instanz (was ein Fall ist, den ich nicht möchte), wenn ich den Status des ProductType-Attributs nicht auf Unverändert festlege. – Furkan

Antwort

11

Sie Ihren Code durch ersetzen:

context.Products.Attach(entity); 
context.Entry(entity).State = System.Data.EntityState.Modified; 

Der Grund, warum dies das gleiche ist (es sei denn, die verbundenen Entitäten wurden bereits vorher an den Kontext in einem anderen Zustand als Unchanged angefügt) ist Attach setzt entityeinschließlich alle verwandten Entitäten in der Objektgrafik in den Kontext im Zustand Unchanged. Wird der Status anschließend auf Modified für entity gesetzt, ändert sich nur der Status des Produkts (nicht der zugehörigen Entitäten) von Unchanged zu Modified.

+0

Dies funktioniert nicht, wenn die Entität aus einem anderen (noch nicht abgelegten) Kontext abgerufen wurde. Sie werden eine Ausnahme dazu bekommen. –

+0

@MichaelLogutov: Sie haben Recht, aber ich bin fast sicher, dass das Szenario in der Frage ein losgelöstes Szenario ist (d. H. Ein früherer Kontext ist bereits entsorgt). Ansonsten ist es sowieso viel einfacher, da Sie normalerweise nicht mit irgendwelchen Entitätszuständen herumhantieren müssen, da die Änderungsverfolgung die korrekten Zustände automatisch verfolgen wird. – Slauma

3

Ok, dann machst du nur etwas falsch. Zusätzlich zu meinem Kommentar habe ich ein Beispiel für Ihr erstellt, das zeigt, dass EF standardmäßig keine Duplikate erstellt.

Ich habe zwei Klassen:

public class Product 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public ProductCategory Category { get; set; } 
    public decimal Price { get; set; } 
} 

public class ProductCategory 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

Ein Kontext:

public class MyContext : DbContext 
{ 
    public DbSet<Product> Products { get; set; } 
    public DbSet<ProductCategory> ProductCategories { get; set; } 

    public MyContext() 
     : base("name=MyContext") 
    { 
    } 

    public MyContext(string nameOrConnectionString) 
     : base(nameOrConnectionString) 
    { 
    } 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     Database.SetInitializer<MyContext>(null); 

     // Table mappings 
     modelBuilder.Entity<Product>().ToTable("Product"); 
     modelBuilder.Entity<ProductCategory>().ToTable("ProductCategory"); 

     base.OnModelCreating(modelBuilder); 
    } 
} 

Dann eine initializer Klasse (dies von anderen Strategien erben könnte, wenn Sie wollen):

public class InitDb<TContext> : DropCreateDatabaseAlways<TContext> 
    where TContext : DbContext 
{ 
} 

Der Hauptprogramm:

static void Main(string[] args) 
    { 
     var prodCat = new ProductCategory() 
     { 
      Name = "Category 1" 
     }; 

     var prod = new Product() 
     { 
      Name = "Product 1", 
      Category = prodCat, 
      Price = 19.95M 
     }; 

     using (var context = new MyContext()) 
     { 
      var initializer = new InitDb<MyContext>(); 
      initializer.InitializeDatabase(context); 

      Console.WriteLine("Adding products and categories to context."); 
      context.ProductCategories.Add(prodCat); 
      context.Products.Add(prod); 

      Console.WriteLine(); 
      Console.WriteLine("Saving initial context."); 
      context.SaveChanges(); 
      Console.WriteLine("Context saved."); 

      Console.WriteLine(); 
      Console.WriteLine("Changing product details."); 
      var initProd = context.Products.Include(x => x.Category).SingleOrDefault(x => x.Id == 1); 
      PrintProduct(initProd); 
      initProd.Name = "Product 1 modified"; 
      initProd.Price = 29.95M; 
      initProd.Category.Name = "Category 1 modified"; 
      PrintProduct(initProd); 

      Console.WriteLine(); 
      Console.WriteLine("Saving modified context."); 
      context.SaveChanges(); 
      Console.WriteLine("Context saved."); 

      Console.WriteLine(); 
      Console.WriteLine("Getting modified product from database."); 
      var modProd = context.Products.Include(x => x.Category).SingleOrDefault(x => x.Id == 1); 
      PrintProduct(modProd); 

      Console.WriteLine(); 
      Console.WriteLine("Finished!"); 
      Console.ReadKey(); 
     } 


    } 

    static void PrintProduct(Product prod) 
    { 
     Console.WriteLine(new string('-', 10)); 
     Console.WriteLine("Id  : {0}", prod.Id); 
     Console.WriteLine("Name : {0}", prod.Name); 
     Console.WriteLine("Price : {0}", prod.Price); 
     Console.WriteLine("CatId : {0}", prod.Category.Id); 
     Console.WriteLine("CatName : {0}", prod.Category.Name); 
     Console.WriteLine(new string('-', 10)); 
    } 

Daraus ergibt sich die folgende Ausgabe der Konsole:

Adding products and categories to context. 

Saving initial context. 
Context saved. 

Changing product details. 
---------- 
Id  : 1 
Name : Product 1 
Price : 19,95 
CatId : 1 
CatName : Category 1 
---------- 
---------- 
Id  : 1 
Name : Product 1 modified 
Price : 29,95 
CatId : 1 
CatName : Category 1 modified 
---------- 

Saving modified context. 
Context saved. 

Getting modified product from database. 
---------- 
Id  : 1 
Name : Product 1 modified 
Price : 29,95 
CatId : 1 
CatName : Category 1 modified 
---------- 

Finished! 

Auch wenn in SQL Server Management Studio, diese Lösung hat nur erstellt (und aktualisiert) ein Produkt und eine Kategorie suchen.

Natürlich sollten Sie mit Repositories arbeiten, um Ihre Daten und eine Arbeitseinheit abzurufen, zu aktualisieren und zu löschen. Diese wurden aus dem Beispiel weggelassen.

Also, wenn Sie keinen Code schreiben, können wir Ihnen nicht viel weiter helfen :-)

+0

der oben genannte Code ist EF-Code ersten Ansatz? – Thomas

+0

@Thomas: Es ist in der Tat Code zuerst. – Nullius