1

我有以下 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。

4

2 回答 2

2

您定义相同关系的两种方法 - 一次使用外键属性,HasForeignKey一次没有这样的属性- 更改外键关联和独立关联MapKey之间的关系类型。

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

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

您的关系是根据需要指定的,这也意味着 EF 希望将导航属性设置为实体并抱怨一个null值。这就是异常的意思。

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

因此,为了使您的代码与独立关联一起工作并摆脱异常,您需要设置导航属性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();

编辑

顺便说一句:您应该能够使用单个数据库查询而不是两个查询:

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();
于 2012-12-14T18:59:13.197 回答
0

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

add constraint Files_PackId_FK foreign key (PackId) references dbo.Packs(Id) on delete cascade on update cascade;
于 2012-12-14T12:31:15.623 回答