2013-07-22 15 views
9

私の最後のプロジェクトでは、Entity Framework 5 Code Firstを使用しました。私は自分のプロジェクトを完了しましたが、開発プロセス中に苦労しました。Entity Frameworkでエンティティを追加および更新する

私は以下の私の痛みを説明しようとした:

私はプロダクト、ProductCategory、注文、会社、メーカーなどのように私のデータアクセスロジック層内のいくつかのデータクラスを持っていた...各クラスが他のクラスにいくつかの参照を持っています。たとえば、ProductインスタンスにはProductCategoryプロパティがあります。

データアクセスオブジェクトクラスの内部では、(プリミティブ型以外の)各プロパティの状態をコンテキストで[変更なし]または[変更済み]に設定しました。以下はいくつかの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; 
    } 
} 

このコードはこのようになります。私の追加メソッドや更新メソッドには、約30行のコードがあります。私は何か間違ったことをしていると思います。エンティティを追加したり更新したりするのは苦痛ではないはずですが、オブジェクトの状態を設定しないと、例外が発生したり、データベースにエントリが重複したりします。私は本当にデータベースにマップする各プロパティの状態を設定する必要がありますか?

+0

データを作成するコードを追加できますか?クラス内の参照が正しい場合、EFはすべてのインスタンスを一度作成する必要があります。 – Nullius

+0

はい、すべてのインスタンスを一度に作成します。それは実際問題です。問題をもう一度説明しましょう。 ProductTypeインスタンスが内部にあるProductインスタンスがあるとします。 Productインスタンスが既に過去に永続化されているとします。そのProductインスタンスを更新しようとすると、ProductType属性の状態をUnchangedに設定しないと、EFは重複したProductTypeインスタンスを作成します。 – Furkan

答えて

11

更に多くのお手伝いをすることはできません。

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

これは同じである理由を(関連エンティティがすでにUnchanged以外の状態のコンテキストに既にアタッチされている場合を除き)Attachは、Unchangedのコンテキストにオブジェクトグラフ内のすべての関連エンティティを含むentityと置き換えます。 entityについて状態をModifiedに設定すると、UnchangedからModifiedまでの製品の状態のみが変更されます(関連エンティティではありません)。

+0

エンティティが別の(まだ廃棄されていない)コンテキストから取得された場合、これは機能しません。これについては例外があります。 –

+0

@MichaelLogutov:あなたはそうですが、問題のシナリオが切り離されたシナリオ(つまり、以前のコンテキストが既に破棄されているシナリオ)であることはほぼ確実です。それ以外の場合は、変更トラッキングが正しい状態を自動的に追跡するため、エンティティの状態をまったく知る必要がないので、とにかく簡単です。 – Slauma

3

あなたは何か間違っているだけです。 私のコメントに加えて、EFがデフォルトで重複を作成しないことを示すサンプルを作成しました。

私は2つのクラスがあります。

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

ワンコンテキスト:

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

そしてある初期化子クラス(あなたがしたい場合、これは他の戦略を継承できます):

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

メインプログラム:

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

これは、次のコンソール出力になります。SQL Server Management Studioの中で見たときに

また
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! 

、この解決策は、1つの製品のみと一つのカテゴリを作成した(および更新)しました。

もちろん、リポジトリを使用してデータと作業単位を取得、更新、削除する必要があります。これらは例から除外されています。だから、

、あなたが任意のコードを投稿していない場合、我々はあなたがして、コードを置き換えることができます:-)

+0

あなたのコードはEFコードの最初のアプローチですか? – Thomas

+0

@トーマス:それは確かに最初のコードです。 – Nullius

関連する問題