删除可选相关实体时,如何将FK更新为空 [英] How to update FK to null when deleting optional related entity

查看:127
本文介绍了删除可选相关实体时,如何将FK更新为空的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于EF来说,我很新,并且为了删除我的对象而努力一点。我的两个对象和关联的DbContext如下所示:

  public class Context:DbContext 
{
public Context ():base(){}
public DbSet< Person>人{get; set;}
public DbSet< Vehicle>车辆{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;}
}

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



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



我的问题是删除Person对象。我通常会如下删除对象:

  private void DeletePerson()
{
using var context = new Context())
{
int personID = 4; //通过其他代码确定
var person = context.Persons.Find(personID);
context.Persons.Remove(person);
context.SaveChanges();
}
}

然而,上述代码将失败,给我一个参考约束异常(由于车辆外键)。我可以预期与特定人员挂钩的所有车辆的外键只能被设置为null?



我能够通过明确加载来获取代码所有相关车辆的上下文如下:

  private void DeletePerson()
{
using context = new Context())
{
//通过其他代码确定
int personID = 4;
//与这个人有关的单车,可以有多个车辆
int vehicleID = 6;

var person = context.Persons.Find(personID);
//似乎强制加载车辆,方便设置
//其PersonID属性为空
var vehicle = context.Vehicles.Find(vehicleID);
context.Persons.Remove(person);
context.SaveChanges();
}
}

然而,上述代码的问题是,我需要在我的Person类中创建一个包含对所有潜在依赖对象的引用(或ID)的List对象(车辆只是这里的一个示例,将有与Person类似关系的各种其他类似类)。



在Person对象中创建此列表是唯一的方法吗?有没有办法自动创建这个列表/自动添加家属?我更喜欢不需要通过我的Person类中的列表对象来明确地管理这些关系。



谢谢!

解决方案

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



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

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

然后,如果您调用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>人{get; set;}
public DbSet< Vehicle>车辆{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;组; }
public int? PersonId {get;组; }
public virtual Person Person {get; set;}
}

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

  class Program 
{
static void Main(string [] args)
{
using(var ctx = new SampleDbContext())
{
Console.WriteLine(创建John McFlanagan及其2车辆);
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();
}

使用(var ctx = new SampleDbContext())
{
var person = ctx.Persons.First();
//在上下文中加载相关的车辆
ctx.Vehicles.Where(v => v.PersonId == person.PersonId).Load();
Console.WriteLine(删除人员,取消车辆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] 17 Oct 2015 9:21 PM#
@DabblerNL是的,功能已经在当前的夜间构建中实现。您将不得不使用.OnDelete(DeleteBehavior.SetNull)在模型中明确指定它。


以前的链接已经死了。您可以在此处查看此模型属性的描述: http://www.learnentityframeworkcore .com / convention /一对多关系


I'm reasonably new to EF, and struggling a little to facilitate deleting my objects. My two objects and associated DbContext look as follows:

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

As per above, one person can be linked to multiple vehicles. There is no explicit link from the person to the vehicle, but there is a link from the vehicle to a 'parent' person through the foreign key relationship.

I then create various vehicles in my code, and link these to optional person objects (foreign key is nullable).

My question is around deleting Person objects. I usually go about deleting the objects as follows:

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

The above code however would fail, giving me a reference constraint exception (due to the vehicle foreign key). I however would have expected the foreign key of all vehicles linked to the specific person to simply be set to null?

I was able to get the code to work by explicitly loading all relevant vehicles to the context as follows:

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

The problem with the above code however, is that I need to create a List object inside my Person class that contains a reference (or ID) to all potential dependent objects (vehicles is just one example here, there will be various other similar classes with similar relationships to Person).

Is the creation of this List in the Person object the only way of doing this? And is there some way of automating the creation of this list / automating the adding of the dependents? I'd prefer to not have to explicitly manage these relationships through a list object in my Person class.

Thanks!

解决方案

Although SQL Server supports it, as you have guessed, EF is not able to set a cascading rule to nullify the FK when the related object is deleted: Entity Framework: Set Delete Rule with CodeFirst

So you need to include in the context the related objects, so that when you delete the Person the related vehicles are updated with a null PersonId. You don't need to include a list for this. You can make the DbContext aware of the related entities like this:

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

Then, if you call delete, it will work as expected.

This is a sample DbContext, configured with fluent API, which works as expected:

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

And this console app shows the expected behavior:

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

    }
}

In (EF7) EF Core it's possible to set the behaviour

Thanks to @Dabblernl comment: http://blogs.msdn.com/b/adonet/archive/2015/10/15/ef7-beta-8-available.aspx#comments

Diego B Vega [MSFT] 17 Oct 2015 9:21 PM # @DabblerNL yes, the functionality is already implemented in current nightly builds. You will have to explicitly specify it in the model using .OnDelete(DeleteBehavior.SetNull).

The previous link is dead. You can see the description of this model property here: http://www.learnentityframeworkcore.com/conventions/one-to-many-relationship

这篇关于删除可选相关实体时,如何将FK更新为空的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆