2013-07-22 28 views
9

W moim ostatnim projekcie użyłem Entity Framework 5 Code First. Ukończyłem mój projekt, ale odczułem wiele bólu podczas procesu tworzenia.Dodawanie i aktualizowanie jednostek za pomocą Entity Framework

starałem się wyjaśnić mój ból poniżej:

miałem kilka klas danych w moim logiki warstwy dostępu do danych produktu podobnego, ProductCategory, porządek, Company, producenta itd ... Każda klasa ma kilka odniesień do innych klas . Na przykład instancja produktu ma właściwość ProductCategory.

Wewnętrzne metody dodawania i aktualizowania moich klas obiektów dostępu do danych Ustawiam stan każdej właściwości (innej niż typy pierwotne) w niezmienionej lub zmodyfikowanej w kontekście. Poniżej znajduje się część metody aktualizacji jakiejś klasy dao:

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; 
    } 
} 

Ten kod wygląda następująco. Niektóre z moich metod dodawania lub aktualizacji to około 30 linii kodu. Myślę, że robię coś nie tak, dodawanie lub aktualizowanie obiektu nie powinno być tak wielkim bólem, ale gdy nie ustawiam stanów obiektów, dostaję wyjątek lub duplikuję wpisy w bazie danych. Czy naprawdę muszę ustawić stan każdej właściwości, która mapuje do bazy danych?

+0

Czy możesz dodać kod, w którym tworzysz dane? Jeśli twoje referencje na twoich zajęciach są poprawne, EF powinno tworzyć wszystkie instancje tylko jeden raz. – Nullius

+0

Tak tworzy wszystkie wystąpienia na raz. W rzeczywistości to jest problem. Pozwól mi ponownie wyjaśnić problem. Załóżmy, że mam instancję produktu, w której znajduje się instancja ProductType. Załóżmy, że instancja produktu już istnieje w przeszłości. Kiedy próbuję zaktualizować tę instancję produktu, EF tworzy zduplikowaną instancję ProductType (co jest przypadkiem, którego nie chcę), jeśli nie ustawię stanu atrybutu ProductType na Unchanged. – Furkan

Odpowiedz

11

można zamienić swój kod przez:

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

Przyczyną tego jest taka sama (o ile powiązane podmioty nie były wcześniej przyłączone do kontekstu w innym stanie niż Unchanged), to Attach umieszcza entity, w tym wszystkie powiązane jednostki w grafie obiektów, w kontekście w stanie Unchanged. Ustawienie stanu na Modified następnie dla entity zmienia tylko sam stan produktu (nie powiązane podmioty) z Unchanged na Modified.

+0

Nie zadziała, jeśli encja została pobrana z innego kontekstu (jeszcze nieudzielonego). Dostaniesz wyjątek na ten temat. –

+0

@MichaelLogutov: Masz rację, ale jestem niemal pewien, że scenariusz w pytaniu jest oderwanym scenariuszem (to znaczy wcześniejszy kontekst został już usunięty). W przeciwnym razie jest to o wiele łatwiejsze, ponieważ zazwyczaj nie musisz bawić się żadnymi stacjami, ponieważ śledzenie zmian automatycznie śledzi prawidłowe stany. – Slauma

3

OK, po prostu robisz coś złego. Oprócz komentarza utworzyłem próbkę, która pokazuje, że EF nie tworzy domyślnie duplikatów.

Mam dwie klasy:

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; } 
} 

jednym kontekście:

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); 
    } 
} 

Wtedy jedna klasa inicjatora (to może dziedziczyć z innych strategii, jeśli chcesz):

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

The program główny:

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)); 
    } 

Wynika to w następujący wyjścia konsoli:

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! 

Również, gdy patrząc w SQL Server Management Studio, rozwiązanie to stworzyło jedynie (i aktualizowany) jednego produktu i jednej kategorii.

Oczywiście, powinieneś pracować z repozytoriami, aby pobrać, zaktualizować i usunąć swoje dane oraz jednostkę pracy. Zostały one pominięte w przykładzie.

Tak więc, jeśli nie pisać żadnego kodu, nie możemy pomóc znacznie dalej :-)

+0

powyższy kod jest pierwszym podejściem do kodu EF? – Thomas

+0

@Thomas: To rzeczywiście kod pierwszy. – Nullius