我有以下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。
答案 0 :(得分:2)
您定义相同关系的两种方式 - 一次使用外键属性和HasForeignKey
,一次没有这样的属性和MapKey
- 更改Foreign Key Association and Independent Association之间的关系类型。 / p>
使用外键关联,您可以通过设置标量属性来指定关系,即外键属性File.PackId
。这个(不可为空)属性总是有一个值,无论你是否明确设置它。至少它有一个默认值0
。使用外键关联,您无需设置导航属性File.Pack
,以告知EF Pack
File
所指的Pack
。 FK属性值就足够了。
另一方面,当使用独立关联时,您的模型没有外键属性,并且告诉EF与特定File
相关的File.Pack
的唯一方法是设置导航属性{ {1}}。
您的关系被指定为 required ,这也意味着EF希望将导航属性设置为实体并抱怨null
值。这就是异常的含义。
(当你删除父实体时,不要问我为什么要拥有一个相关的实体。我不知道。实际上只有一个SQL {{1必须向父数据库发出父语句。但也许有更深层次的原因。)
因此,为了使您的代码与独立协会合作并摆脱异常,您需要设置导航属性DELETE
:
File.Pack
修改强>
BTW:您应该能够使用单个数据库查询而不是两个查询:
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();
答案 1 :(得分:0)
你的约束中有一个删除级联,这就是它删除这两个实体的原因:
add constraint Files_PackId_FK foreign key (PackId) references dbo.Packs(Id) on delete cascade on update cascade;