如何在删除可选相关实体时将FK更新为null

Use*_*123 11 c# entity-framework cascading-deletes ef-code-first

我是EF的新手,并且为了方便删除我的对象而苦苦挣扎.我的两个对象和相关的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;}
} 
Run Code Online (Sandbox Code Playgroud)

如上所述,一个人可以链接到多个车辆.从人到车辆没有明确的链接,但是通过外键关系存在从车辆到"父母"人的链接.

然后我在我的代码中创建各种车辆,并将这些车辆链接到可选的人物对象(外键可以为空).

我的问题是删除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();
    }
}
Run Code Online (Sandbox Code Playgroud)

但是上面的代码会失败,给我一个引用约束异常(由于车辆外键).但是我原本期望链接到特定人的所有车辆的外键都被设置为空?

通过将所有相关车辆明确加载到上下文中,我能够使代码工作,如下所示:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

然而,对于上述代码的问题是,我需要创建我的Person类内的列表对象,它包含一个引用(或ID)向所有潜在的依赖对象(汽车只是一个例子这里,将会有各种其他类似的班,与人类相似的关系).

在Person对象中创建此List是唯一的方法吗?是否有一些方法可以自动创建此列表/自动添加依赖项?我宁愿不必通过Person类中的列表对象显式管理这些关系.

谢谢!

Jot*_*aBe 9

虽然SQL Server支持它,但正如您所猜测的那样,当删除相关对象时,EF无法设置级联规则来使FK无效:实体框架:使用CodeFirst设置删除规则

所以你需要在上下文中包含相关的对象,这样当你删除Person相关的车辆时会更新为null PersonId.您不需要包含此列表.您可以DbContext像这样了解相关实体:

ctx.Vehicles.Where(v => v.PersonId == personId).Load();
Run Code Online (Sandbox Code Playgroud)

然后,如果您调用delete,它将按预期工作.

这是一个DbContext使用流畅的API配置的示例,它按预期工作:

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;}
} 
Run Code Online (Sandbox Code Playgroud)

此控制台应用程序显示预期的行为:

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

    }
}
Run Code Online (Sandbox Code Playgroud)

在(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 #DabblerNL是的,该功能已经在当前的夜间版本中实现.您必须使用.OnDelete(DeleteBehavior.SetNull)在模型中明确指定它.

之前的链接已经死了.您可以在此处查看此模型属性的说明:http://www.learnentityframeworkcore.com/conventions/one-to-many-relationship

  • @zolty13我认为它清楚地解释了对于 EF 6.x 这是不可能的,但对于 EF Core 是不可能的(它可以设置“ON DELETE”行为,该行为在服务端自动发生)。如果您认为这个答案有用,请投票。 (2认同)