web-dev-qa-db-ja.com

オプションの関連エンティティを削除するときにFKをnullに更新する方法

私はEFにかなり慣れていないので、オブジェクトの削除を容易にするために少し苦労しています。私の2つのオブジェクトと関連するDbContextは次のようになります。

public class Context: DbContext
{
    public Context() : base(){}
    public DbSet<Person> Persons {get;set;}
    public DbSet<Vehicle> Vehicles {get;set;}
}

public class Person
{
   public int PersonID {get;set;}
   public string Name {get;set;}
}

public class Vehicle
{
   public int VehicleID {get;set;}

   public int? PersonID {get;set;}

   [ForeignKey("PersonID")]
   public virtual Person Person {get;set;}
} 

上記のように、1人が複数の車両にリンクできます。人から車両への明示的なリンクはありませんが、外部キー関係を介した車両から「親」の人へのリンクはあります。

次に、コードでさまざまな車両を作成し、それらをオプション人のオブジェクトにリンクします(外部キーはnull可能です)。

私の質問は、Personオブジェクトの削除に関するものです。私は通常、次のようにオブジェクトを削除します。

private void DeletePerson()
{
    using (var context = new Context())
    {
        int personID = 4; //Determined through other code
        var person = context.Persons.Find(personID);
        context.Persons.Remove(person);
        context.SaveChanges();
    }
}

ただし、上記のコードは失敗し、参照制約の例外が発生します(車両の外部キーが原因)。ただし、特定の人物にリンクされているすべての車両の外部キーが単にnullに設定されることを期待していましたか?

次のように、関連するすべての車両をコンテキストに明示的にロードすることで、コードを機能させることができました。

private void DeletePerson()
{
    using (var context = new Context())
    {
        //Determined through other code
        int personID = 4; 
        // Single vehicle associated with this person, there can be multiple vehicles
        int vehicleID = 6; 

        var person = context.Persons.Find(personID);
        // Seems to force loading of the vehicle, facilitating setting 
        // its "PersonID" property to null
        var vehicle = context.Vehicles.Find(vehicleID); 
        context.Persons.Remove(person);
        context.SaveChanges();
    }
}

ただし、上記のコードの問題は、すべての潜在的な依存オブジェクトへの参照(またはID)を含むListオブジェクトをPersonクラス内に作成する必要があることです(車両はここではほんの一例であり、他のさまざまな同様のクラスがあります人と同様の関係)。

Personオブジェクトにこのリストを作成することがこれを行う唯一の方法ですか?そして、このリストの作成を自動化する/扶養家族の追加を自動化する方法はありますか? Personクラスのリストオブジェクトを介してこれらの関係を明示的に管理する必要はありません。

ありがとう!

12
User_FSharp1123

SQL Serverはこれをサポートしていますが、ご想像のとおり、EFは、関連オブジェクトが削除されたときにFKを無効にするカスケードルールを設定できません。 Entity Framework:Set Delete Rule with CodeFirst

したがって、コンテキストに関連オブジェクトを含める必要があります。これにより、Personを削除すると、関連車両がnull PersonIdで更新されます。このためのリストを含める必要はありません。 DbContextに、次のような関連エンティティを認識させることができます。

ctx.Vehicles.Where(v => v.PersonId == personId).Load();

次に、deleteを呼び出すと、期待どおりに機能します。

これは、流暢なAPIで構成されたサンプルDbContextであり、期待どおりに機能します。

public class SampleDbContext: DbContext
{
    public SampleDbContext()
        : base("name=CascadeOnDelete")
    {

    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Vehicle>()
            .HasOptional(v => v.Person)
            .WithMany()
            .HasForeignKey(v => v.PersonId);
            //.WillCascadeOnDelete();
        base.OnModelCreating(modelBuilder);
    }

    public DbSet<Person> Persons {get;set;}
    public DbSet<Vehicle> Vehicles {get;set;}
}

public class Person
{
    public int PersonId {get;set;}
    public string Name {get;set;}
}

public class Vehicle
{
    public int VehicleId {get;set;}
    public string Model { get; set; }
    public int? PersonId { get; set; }
    public virtual Person Person {get;set;}
} 

そして、このコンソールアプリは期待される動作を示しています。

class Program
{
    static void Main(string[] args)
    {
        using (var ctx = new SampleDbContext())
        {
            Console.WriteLine("Creating John McFlanagan and their 2 vehicles");
            var person = new Person {Name = "John McFlanagan"};
            var vehicle1 = new Vehicle { Person = person, Model = "Vauxhall Astra" };
            var vehicle2 = new Vehicle { Person = person, Model = "Ford Capri" };

            ctx.Vehicles.AddRange(new[] {vehicle1, vehicle2});
            ctx.SaveChanges();
        }

        using (var ctx = new SampleDbContext())
        {
            var person = ctx.Persons.First();
            // Loading related vehicles in the context
            ctx.Vehicles.Where(v => v.PersonId == person.PersonId).Load();
            Console.WriteLine("Deleting the person, and nullifying vehicles PersonId");
            ctx.Persons.Remove(person);
            ctx.SaveChanges();
        }

    }
}

(EF7)EF Coreでは、動作を設定することができます

@Dabblernlコメントに感謝します: http://blogs.msdn.com/b/adonet/archive/2015/10/15/ef7-beta-8-available.aspx#comments

Diego B Vega [MSFT] 2015年10月17日9:21 PM#@ DabblerNLはい、機能は現在のナイトリービルドにすでに実装されています。-を使用してモデルで明示的に指定する必要があります。 OnDelete(DeleteBehavior.SetNull)

前のリンクは無効です。このモデルプロパティの説明はここで確認できます: http://www.learnentityframeworkcore.com/conventions/one-to-many-relationship

9
JotaBe