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

Neu*_*ynx 3 entity-framework data-annotations entity-framework-6

我想将VM_hostname,datetime和name属性作为Disk类的复合键。同时,Disk类的 VM_hostname和datetime 应该引用VirtualMachine类的VM_hostname和datetime (即外键)。

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

有人知道吗?另外,请注意,即时消息使用数据注释。

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; }


}
Run Code Online (Sandbox Code Playgroud)

Ger*_*old 5

您的问题与我建议重复的问题之间的主要区别是您的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; }
} 
Run Code Online (Sandbox Code Playgroud)

可以使用三个选项来设置映射。

选项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; }
}
Run Code Online (Sandbox Code Playgroud)

如您所见,此处的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; }
}
Run Code Online (Sandbox Code Playgroud)

InverseProperty从关系一端的类型中的一个或多个属性指向关系另一端的类型中的导航属性的指针。实现相同映射的另一种方法是将应用于[InverseProperty("Parent")]的两个关键属性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 });
Run Code Online (Sandbox Code Playgroud)

如评论中所述,流利的映射比数据注释更不容易出错。数据批注提供了太多的选项来配置映射,而且并不总是很容易看到连接的部分。这就是为什么我喜欢流利的映射。

实体框架核心

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

实体类型“父母”具有使用数据注释定义的复合主键。要设置复合主键,请使用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 });
Run Code Online (Sandbox Code Playgroud)