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

时间:2017-03-15 19:48:13

标签: entity-framework entity-framework-6 data-annotations

我想将VM_hostname,datetime和name属性设置为磁盘类的复合键。同时,磁盘类的VM_hostname和日期时间应引用 VirtualMachine 类的VM_hostname和日期时间(即外键)。

我做了这个,但它给了我这个例外: 属性' datetime'上的ForeignKeyAttribute在类型' WebJob1.Historical.Disk'无效。导航属性'日期时间'在依赖类型' WebJob1.Historical.Disk'上找不到。 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; }


}

1 个答案:

答案 0 :(得分:5)

您的问题与我建议的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从关系一端的类型中的一个或多个属性指向关系另一端类型中的导航属性。实现相同映射的另一种方法是在[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 });

如评论中所述,流畅的映射比数据注释更不容易出错。数据注释提供了太多选项来配置映射,并且不容易看到哪些部分已连接。这就是我最喜欢的流利映射的原因。

实体框架核心

在EF-core(当前版本2.2.1)中,复合主键无法通过数据注释建模。它会引发运行时异常:

  

实体类型“Parent”具有使用数据注释定义的复合主键。要设置复合主键,请使用流畅的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 });