未定义FK属性时,未能删除子项



我有以下SQL表(为了简单起见,删除了列):

create table dbo.Packs 
(
Id int identity not null
constraint Packs_Id_PK primary key clustered (Id)
);
create table dbo.Files
(
Id int identity not null
constraint Files_Id_PK primary key clustered (Id),
PackId int not null
);
alter table dbo.Files
add constraint Files_PackId_FK foreign key (PackId) references dbo.Packs(Id) on delete cascade on update cascade;

然后我创建了如下的Pocos:

public class Pack {
public Int32 Id { get; set; }
public virtual ICollection<File> Files { get; set; }
} // Pack
public class File {
public Int32 Id { get; set; }
public int PackId { get; set; } 
public virtual Pack Pack { get; set; }
} // File

配置为:

internal class PackMapper : EntityTypeConfiguration<Pack> {
internal PackMapper()
: base() {
ToTable("Packs");
HasKey(x => x.Id);
Property(x => x.Id).IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
} // PackMapper
internal class FileMapper : EntityTypeConfiguration<File> {
internal FileMapper()
: base() {
ToTable("Files");
HasKey(x => x.Id);
Property(x => x.Id).IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
// 1 > CONFIGURATION WITH FK IN ENTITY   
Property(x => x.PackId).HasColumnName("PackId").IsRequired();
HasRequired(x => x.Pack).WithMany(x => x.Files).HasForeignKey(x => x.PackId);
// 2 > CONFIGURATION WITHOUT FK IN ENTITY
// HasRequired<Pack>(x => x.Pack).WithMany(y => y.Files).Map(z => { z.MapKey("PackId"); });
}
} // FileMapper

然后我试图删除一个文件:

Pack pack = context.Packs.First(x => x.Id == 31);
IList<Int32> ids = context.Entry<Pack>(pack).Collection(x => x.Files).Query().Select(x => x.Id).ToList();
foreach (int id in ids) {
File file = new File() { Id = id };
context.Files.Attach(file);
context.Files.Remove(file);
}
context.SaveChanges();

如果我使用配置1,文件将被删除。

如果我使用配置2(不需要FK属性),那么我会得到错误:

"Context.Files"中的实体参与"File_Pack"关系。找到了0个相关的"File_Pack_Target"。应为1"File_Pack_Target"。

为什么?在不定义FK特性时,是否需要指定其他内容?

注意:我使用的是EF 5。

定义同一关系的两种方法-一种是使用外键属性和HasForeignKey,另一种是不使用此类属性和MapKey-更改外键关联和独立关联之间的关系类型。

使用外键关联,可以通过设置标量属性来指定关系,即外键属性File.PackId。这个(不可为null)属性总是有一个值,无论您是否显式设置它。至少它有一个默认值0。使用外键关联时,不需要设置导航属性File.Pack来告诉EFFile指的是哪个Pack。FK属性值就足够了。

另一方面,当使用独立关联时,您的模型没有外键属性,并且告诉EF哪个是与特定File相关的Pack的唯一方法是设置导航属性File.Pack

您的关系被指定为必需,这也意味着EF希望将导航属性设置为实体,并抱怨null值。这就是例外的含义。

(当你删除父实体时,不要问我为什么它想要一个相关的实体。我不知道。当只需要向数据库发出父实体的SQLDELETE语句时,这实际上并不重要。但也许还有更深层次的原因。)

因此,为了使您的代码使用独立关联并消除异常,您需要设置导航属性File.Pack:

Pack pack = context.Packs.First(x => x.Id == 31);
IList<Int32> ids = context.Entry<Pack>(pack).Collection(x => x.Files).Query()
.Select(x => x.Id).ToList();
foreach (int id in ids) {
File file = new File() { Id = id, Pack = pack };
context.Files.Attach(file);
context.Files.Remove(file);
}
context.SaveChanges();

编辑

BTW:您应该能够使用单个数据库查询,而不是两个查询:

IList<Int32> ids = context.Files.Where(f => f.Pack.Id == 31)
.Select(f => f.Id).ToList();
Pack pack = new Pack { Id = 31 };
foreach (int id in ids) {
File file = new File { Id = id, Pack = pack };
context.Files.Attach(file);
context.Files.Remove(file);
}
context.SaveChanges();

您的约束中有一个删除级联,这就是它删除两个实体的原因:

add constraint Files_PackId_FK foreign key (PackId) references dbo.Packs(Id) on delete cascade on update cascade;

最新更新