将复合外键映射到复合主键,其中外键也是主键

Mapping composite foreign key to composite primary key where the foreign key is also a primary key

我想将 VM_hostname、日期时间和名称属性作为 Disk class 的复合键。同时VM_hostname和Disk class的datetime应该参考VM_hostname和VirtualMachine的datetime class(即外键)。

我这样做了,但它给了我这个例外: 'WebJob1.Historical.Disk' 类型 属性 'datetime' 上的 ForeignKeyAttribute 无效。在依赖类型 'WebJob1.Historical.Disk' 上找不到导航 属性 'Datetime'。名称值应该是有效的导航 属性 名称

有人知道吗?另外,请注意我使用的是数据注释。

public class VirtualMachine
{

    [Key]
    [Column(Order = 0)]
    public string VM_Hostname { get; set; }
    [Key]
    [Column(Order = 1)]
    public DateTime Datetime;
    public virtual List<Disk> disks { get; set; }
}

 public class Disk
{
    [Key,ForeignKey("VirtualMachine"),Column(Order = 0)]
    public string VM_hostname { get; set; }
    [Key,ForeignKey("Datetime"), Column(Order = 1)]
    public DateTime datetime { get; set; }
    [Key, Column(Order = 2)]
    public string name { get; set; }

    public virtual VirtualMachine VirtualMachine{ get; set; }


}

你的问题和我建议的 duplicate 之间的主要区别是你的 ForeignKey 属性不引用 -

  • 从原始 属性 到导航 属性
  • 从导航属性到原始属性

在您的例子中,引用是从原始 属性 到另一种类型的另一个原始 属性。还有,小细节,VirtualMachine.Datetime应该是属性,不是会员。但我不得不承认,“重复”并没有涵盖你的情况。

所以让我们试着把它变成一个综合的答案如何处理这种情况 Entity Framework 6. 我将使用一个抽象的模型来解释各种选项:

public class Parent
{
    public int Id1 { get; set; } // Key
    public int Id2 { get; set; } // Key   
    public string Name { get; set; }   
    public virtual List<Child> Children { get; set; }
}

public class Child
{
    public int Id1 { get; set; } // Key
    public int Id2 { get; set; } // Key
    public int Id3 { get; set; } // Key
    public string Name { get; set; }
    public virtual Parent Parent { get; set; }
} 

设置映射有三个选项。

选项 1

数据注释,ForeignKey属性:

public class Parent
{
    [Key]
    [Column(Order = 1)]
    public int Id1 { get; set; }
    [Key]
    [Column(Order = 2)]
    public int Id2 { get; set; }
    
    public string Name { get; set; }

    public virtual List<Child> Children { get; set; }
}

public class Child
{
    [Key]
    [Column(Order = 0)]
    public int Id1 { get; set; }
    [Key]
    [Column(Order = 1)]
    public int Id2 { get; set; }
    [Key]
    [Column(Order = 2)]
    public int Id3 { get; set; }

    public string Name { get; set; }

    [ForeignKey("Id1,Id2")]
    public virtual Parent Parent { get; set; }
}

如您所见,这里的 ForeignKey 属性指的是从导航 属性 到原始属性。此外,列顺序中的绝对数字无关紧要,重要的是它们的顺序。

选项 2

数据注释,InverseProperty属性:

public class Parent
{
    [Key]
    [Column(Order = 1)]
    public int Id1 { get; set; }
    [Key]
    [Column(Order = 2)]
    public int Id2 { get; set; }
    
    public string Name { get; set; }

    public virtual List<Child> Children { get; set; }
}

public class Child
{
    [Key]
    [Column(Order = 0)]
    [InverseProperty("Children")]
    public int Id1 { get; set; }
    [Key]
    [Column(Order = 1)]
    [InverseProperty("Children")]
    public int Id2 { get; set; }
    [Key]
    [Column(Order = 2)]
    public int Id3 { get; set; }

    public string Name { get; set; }

    public virtual Parent Parent { get; set; }
}

InverseProperty 从关系一端类型中的一个或多个属性指向关系另一端类型中的导航 属性。实现相同映射的另一种方法是在 Parent.

的两个关键属性上应用 [InverseProperty("Parent")]

选项 3

流畅的映射:

modelBuilder.Entity<Parent>().HasKey(p => new { p.Id1, p.Id2 });
modelBuilder.Entity<Child>().HasKey(p => new { p.Id1, p.Id2, p.Id3 });
modelBuilder.Entity<Parent>()
    .HasMany(p => p.Children)
    .WithRequired(c => c.Parent)
    .HasForeignKey(c => new { c.Id1, c.Id2 });

正如评论所说,流畅的映射比数据注释更不容易出错。数据注释提供了太多配置映射的选项,并且并不总是很容易看出哪些部分是连接的。这就是为什么流利的映射是我的最爱。

Entity Framework核心

在 EF-core(当前版本 3.1.6)中,复合主键无法通过数据注释建模。它抛出 运行 次异常:

Entity type 'Parent' has composite primary key defined with data annotations. To set composite primary key, use fluent API.

所以对于 EF-core 只有选项 3 是可行的。映射几乎相同:

modelBuilder.Entity<Parent>().HasKey(p => new { p.Id1, p.Id2 });
modelBuilder.Entity<Child>().HasKey(p => new { p.Id1, p.Id2, p.Id3 });
modelBuilder.Entity<Parent>()
    .HasMany(p => p.Children)
    .WithOne(c => c.Parent) // Different here
    .HasForeignKey(c => new { c.Id1, c.Id2 });