删除可选相关实体时如何将 FK 更新为 null
How to update FK to null when deleting optional related entity
我是 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;}
}
同上,一个人可以link乘坐多辆车。从人到车没有显式的link,但是从车到'parent'人通过外键关系有linklink。
然后我在我的代码中创建各种车辆,并且 link 这些到 optional person 对象(外键可以为 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();
}
}
但是上面的代码会失败,给我一个引用约束异常(由于车辆外键)。然而,我本来希望所有车辆的外键 linked 到特定的人只是简单地设置为 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();
}
}
然而,上面代码的问题是我需要在我的 Person class 中创建一个 List 对象,其中包含对所有潜在依赖对象的引用(或 ID)(车辆只是这里的一个例子) , 将会有其他各种与 Person 具有相似关系的相似 classes)。
在 Person 对象中创建这个 List 是唯一的方法吗?是否有某种方法可以自动创建此列表/自动添加家属?我宁愿不必通过我的 Person class.
中的列表对象显式管理这些关系
谢谢!
虽然SQL服务器支持,但是如你所料,EF在删除相关对象时无法设置级联规则使FK无效:Entity Framework: Set Delete Rule with CodeFirst
所以您需要在上下文中包含相关对象,这样当您删除 Person
时,相关车辆会更新为空值 PersonId
。您不需要为此包含一个列表。您可以像这样让 DbContext
了解相关实体:
ctx.Vehicles.Where(v => v.PersonId == personId).Load();
然后,如果您调用 delete,它将按预期工作。
这是一个示例 DbContext
,配置了 fluent 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;}
}
并且此控制台应用显示了预期的行为:
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] 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).
之前的link已经死了。您可以在此处查看此模型 属性 的描述:http://www.learnentityframeworkcore.com/conventions/one-to-many-relationship
除了 的答案之外,还可以执行 Include(...)
来加载相关实体。
喜欢:
var person = ctx.Persons.Include(p => p.Vehicles).First();
Console.WriteLine("Deleting the person, and nullifying vehicles PersonId");
ctx.Persons.Remove(person);
ctx.SaveChanges();
我是 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;}
}
同上,一个人可以link乘坐多辆车。从人到车没有显式的link,但是从车到'parent'人通过外键关系有linklink。
然后我在我的代码中创建各种车辆,并且 link 这些到 optional person 对象(外键可以为 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();
}
}
但是上面的代码会失败,给我一个引用约束异常(由于车辆外键)。然而,我本来希望所有车辆的外键 linked 到特定的人只是简单地设置为 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();
}
}
然而,上面代码的问题是我需要在我的 Person class 中创建一个 List 对象,其中包含对所有潜在依赖对象的引用(或 ID)(车辆只是这里的一个例子) , 将会有其他各种与 Person 具有相似关系的相似 classes)。
在 Person 对象中创建这个 List 是唯一的方法吗?是否有某种方法可以自动创建此列表/自动添加家属?我宁愿不必通过我的 Person class.
中的列表对象显式管理这些关系谢谢!
虽然SQL服务器支持,但是如你所料,EF在删除相关对象时无法设置级联规则使FK无效:Entity Framework: Set Delete Rule with CodeFirst
所以您需要在上下文中包含相关对象,这样当您删除 Person
时,相关车辆会更新为空值 PersonId
。您不需要为此包含一个列表。您可以像这样让 DbContext
了解相关实体:
ctx.Vehicles.Where(v => v.PersonId == personId).Load();
然后,如果您调用 delete,它将按预期工作。
这是一个示例 DbContext
,配置了 fluent 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;}
}
并且此控制台应用显示了预期的行为:
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] 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).
之前的link已经死了。您可以在此处查看此模型 属性 的描述:http://www.learnentityframeworkcore.com/conventions/one-to-many-relationship
除了 Include(...)
来加载相关实体。
喜欢:
var person = ctx.Persons.Include(p => p.Vehicles).First();
Console.WriteLine("Deleting the person, and nullifying vehicles PersonId");
ctx.Persons.Remove(person);
ctx.SaveChanges();