8

私は、Entity Framework 5.0 e SQL Server CE 4.0で.NET4.0アプリケーションを使用しています。1対多の関係の子を持つ親を削除

私は1対多(親/子)の関係を持つ2つのエンティティを持っています。私は親削除の削除をカスケードするように設定しましたが、なんらかの理由でうまく動作していないようです。ここで

は私のエンティティの簡易版である:

public class Account 
    { 
     public int AccountKey { get; set; } 
     public string Name { get; set; } 

     public ICollection<User> Users { get; set; } 
    } 

    internal class AccountMap : EntityTypeConfiguration<Account> 
    { 
     public AccountMap() 
     { 
      this.HasKey(e => e.AccountKey); 
      this.Property(e => e.AccountKey).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); 
      this.Property(e => e.Name).IsRequired(); 
     } 
    } 


    public class User 
    { 
     public int UserKey { get; set; } 
     public string Name { get; set; } 

     public Account Account { get; set; } 
     public int AccountKey { get; set; } 
    } 

    internal class UserMap : EntityTypeConfiguration<User> 
    { 
     public UserMap() 
     { 
      this.HasKey(e => e.UserKey); 
      this.Property(e => e.UserKey).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); 
      this.Property(e => e.Name).IsRequired(); 


      this.HasRequired(e => e.Account) 
       .WithMany(e => e.Users) 
       .HasForeignKey(e => e.AccountKey); 
     } 
    } 

    public class TestContext : DbContext 
    { 
     public TestContext() 
     { 
      this.Configuration.LazyLoadingEnabled = false; 
     } 

     public DbSet<User> Users { get; set; } 
     public DbSet<Account> Accounts { get; set; } 

     protected override void OnModelCreating(DbModelBuilder modelBuilder) 
     { 
      modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); modelBuilder.Conventions.Remove<StoreGeneratedIdentityKeyConvention>(); 
      modelBuilder.LoadConfigurations(); 
     } 

    } 

接続文字列:

<connectionStrings> 
    <add name="TestContext" connectionString="Data Source=|DataDirectory|\TestDb.sdf;" providerName="System.Data.SqlServerCe.4.0" /> 
    </connectionStrings> 

そして、私のアプリのワークフローを簡略化したバージョン:

static void Main(string[] args) 
{ 
    try 
    { 
     Database.SetInitializer(new DropCreateDatabaseAlways<TestContext>()); 
     using (var context = new TestContext()) 
      context.Database.Initialize(false); 

     Account account = null; 
     using (var context = new TestContext()) 
     { 
      var account1 = new Account() { Name = "Account1^" }; 
      var user1 = new User() { Name = "User1", Account = account1 }; 

      context.Accounts.Add(account1); 
      context.Users.Add(user1); 

      context.SaveChanges(); 

      account = account1; 
     } 

     using (var context = new TestContext()) 
     { 
      context.Entry(account).State = EntityState.Deleted; 
        context.SaveChanges(); 
     } 
    } 
    catch (Exception e) 
    { 
     Console.WriteLine(e.ToString()); 
    } 

    Console.WriteLine("\nPress any key to exit..."); 
    Console.ReadLine(); 
} 

私がしよう親エンティティを削除するには、

の1つまたは複数の外部キーのプロパティがnullに設定できないため、関係を変更できませんでした。 関係が変更されると、関連する外部キー・プロパティーはNULL値に設定されます。 外部キーがNULL値をサポートしていない場合は、新しい関係 を定義する必要があります。外部キーのプロパティに別の の値を割り当てないと、関連のないオブジェクトを削除する必要があります。

私の関係設定はok(followed the documentation)と思われます。私もguidelines on deleting detached entitiesを探しました。

私は本当にその削除が機能しない理由を理解できません。私はすべての子をロードすることを避け、それらを一つずつ削除し、親を削除することを望みます。それよりも良い解決策が必要であるからです。

答えて

12

エンティティの状態をDeletedに設定し、このエンティティに対してDbSet<T>.Removeを呼び出すことは同じではありません。

違いは関係が削除カスケードで構成されている場合Removeはこれを行いながら、状態を設定することだけDeletedではなく、関連するエンティティの状態にルートエンティティ(あなたがcontext.Entryに渡す1)の状態を変更することです。

実際に例外が発生した場合は、子に(すべてまたは一部のみ)コンテキストに関連付けられているかどうかによって異なります。これは従うことはやや困難である行動につながる:

  • あなたが呼び出す場合

    Removeあなたが例外を取得しない、子供がロードされているかどうかに関係なく。違いはまだあり:
    • 子供たちは、コンテキストに接続されている場合、EFは、親のために、すべての付属子供のためDELETE文が生成されます(RemoveDeletedとしてそれらすべてをマークしましたので)
    • がいる場合子はコンテキストに関連付けられていませんEFは親に対してデータベースにDELETE文を送信し、カスケード削除が有効になっているためデータベースは子も削除します。あなたはDeletedにルートエンティティの状態を設定した場合
  • あなたはおそらくは例外を取得することができます。
    • 子どもたちは、自分の状態がDeletedとEF意志に設定されることはありませんコンテキストに接続されている場合(子)を削除せずに、または少なくとも外部キーをDeleted状態にない別のルートエンティティに設定することなく、プリンシパル(ルートエンティティ)を必要な関係で削除しようとしていると不平を言っています。それはあなたが持っていた例外です:accountは、ルートであるとuser1accountの依存であり、また、コンテキストに状態Unchangeduser1を添付しますcontext.Entry(account).State = EntityState.Deleted;を呼び出す(またはそれを行いますSaveChangesで検出を変更、私はその当接わかりません)。リレーションシップフィックスアップはコードに明示的に追加していませんが、最初のコンテキストでコレクションに追加したため、user1account.Usersコレクションの一部です。
    • コンテキスト設定に子が接続されていない場合、ルートの状態はDeletedになり、DELETE文がデータベースに送信され、データベース内のカスケード削除によって子も削除されます。これは例外なく動作します。たとえば、account.Users = nullを設定してから、2番目のコンテキストでDeletedに状態を設定する前、または2番目のコンテキストを入力する前に、コードが機能します。 Remove ...

      using (var context = new TestContext()) 
      { 
          context.Accounts.Attach(account); 
          context.Accounts.Remove(account); 
          context.SaveChanges(); 
      } 
      

      を使用して、私の意見では

... Removeの動作がはるかにあなたがカスケードで、必要な関係を期待するようなものですので、明らかれる好ましい方法です。削除(あなたのモデルの場合です)。手動の状態変更の動作が他のエンティティの状態に依存することにより、使用がより困難になります。特別な場合に限って高度な使い方として考えています。

この相違点は広く知られていないか、文書化されていません。私はそれについての記事はほとんど見ていない。もう一度見つけることができる唯一のものはthis one by Zeeshan Hiraniです。

+1

非常に啓発的な@Slauma!私は質問を投稿する前にいくつか手がかりをたくさん探しましたが、あなたが言及した投稿は見つかりませんでした。ありがとうございます –

+0

EFの詳細を読むたびに、挿入、更新、削除などのEFで親子操作を行うと、多くの人が非常に多くの問題を抱えているようですが、失望します。 –

1

私は少し異なるアプローチを試みましたが、不思議なことに、それはうまくいきました。これにより、

using (var context = new TestContext()) 
{ 
    context.Entry(account).State = EntityState.Deleted; 
    context.SaveChanges(); 
} 

:私はこのコードを交換する場合

using (var context = new TestContext()) 
{ 
    context.Entry(account).State = EntityState.Unchanged; 
    context.Accounts.Remove(account); 
    context.SaveChanges(); 
} 

それはなし、さらに問題で動作します。これがバグか何かが欠落しているかどうかはわかりません。最初の方法(EntityState.Deleted)が推奨された方法であると確信していたので、私は本当にこの問題についていくつかの光を感謝しました。

関連する問題