实体框架:在唯一(非主键)父字段上的复合外键

时间:2014-02-05 14:05:22

标签: c# .net entity-framework

我有一个表需要通过其Id列绑定到另一个表,并通过其(Id, Code)列绑定到第三个表。我可以使用主键和唯一索引在 SQL Server 中轻松完成,但不知道如何在 Entity Framework 中实现它。

如果我在两列上都添加了[Key]属性,那么我就无法创建第一个关系。

否则,如果我只申请一个[Key],那么我就无法建立第二关系。

有没有解决方法?

1 个答案:

答案 0 :(得分:2)

好的,我可以使用 Fluent API 制作所需数量的密钥。它现在有效。

更详细:
在商业术语中,我有一个Form对象,其字段就像在html文档中一样 - 类型为input, choice, text, radiogroup等。
我更喜欢在单独的表中存储强类型的字段数据,但是表单需要知道哪些字段用于维护它们的唯一顺序。

所以在数据库中我有表:

Form - 包含表格的名称和说明
FormField - (每个表单0到多行)包含 FormId FieldType OrderNo 。通过将(FormId,OrderNo)设置为唯一,我保持正确的顺序。

诀窍是键入的字段本身存储在不同的表中,如InputFieldChoiceFieldNumberField等。我希望它们绑定到父FormField行,如0或删除父行时,不允许它们丢失 如果它是简单的父子关系,我可以使用标准FK,但在这里我有variuos子表。

为了使其一致,在 SQL Server 中,我在父FormField表和FK中创建了一个复合PK (FormId,FieldType) as {em>(FormId,FieldType)在所有子表中FieldType设置为常量计算值(例如InputField表中为0,ChileField表中为1等)。
因此,对于单个FormField和多个子特定类型的表,我有一个良好的父子约束 在EF方法中,要制作复合FK,您需要将所有相应的字段设置为键,因此您将[Key]属性放在两个字段上。
但是,如果特定于类型的表需要拥有自己的子项(例如ChoiceField具有ChoiceOptions),并且您已经有了复合键,那么您需要使用现有的复合键或添加新的keyset(仅包含 Id 字段)。

所以,我的问题是让ChoiceField表有两组键:一组用于父FieldForm行,第二组用于ChoiceOption子表。简单 - 用于ChoiceOption子表。
使用Fluent API我可以添加第二个键。

这是我的EF模型:

[Table("Form")]
public class Form : IEntity
{
    [Key, Column("FormID")]
    public int Id { get; set; }

    [Required, StringLength(50)]
    public string Name { get; set; }

    public ICollection<FormField> FormFields { get; set; }
}

[Table("FormField")]
public class FormField : IEntity
{
    [Key, Column("FormFieldID", Order = 0)]
    public int Id { get; set; }

    [Key, Column(Order = 1)]
    public FieldType FieldType { get; set; }

    [ForeignKey("Form")]
    public int FormId { get; set; }

    public int OrderNo { get; set; }

    public virtual Form Form { get; set; }

    public virtual AddressField AddressField { get; set; }
    public virtual ChoiceField ChoiceField { get; set; }
    public virtual DateTimeField DateTimeField { get; set; }
// etc
}

[Table("ChoiceField")]
public class ChoiceField : BaseField, IEntity
{
    [Key, ForeignKey("FormField"), Column("FormFieldID", Order = 0)] // these keys are for parent FormField table
    public int Id { get; set; }

    [Key, ForeignKey("FormField"), Column(Order = 1)]
    public FieldType FieldType { get; set; }

    public virtual FormField FormField { get; set; }

    public ICollection<ChoiceFieldOption> ChoiceFieldOptions { get; set; }
}

[Table("ChoiceFieldOption")]
public class ChoiceFieldOption : IEntity
{
    [Key, Column("ChoiceFieldOptionID")]
    public int Id { get; set; }

    [ForeignKey("ChoiceField")] // this FK are bound to simple ChoiceField.Id key defined in fluent API
    public int ChoiceFieldId { get; set; }

    [Required, StringLength(50)]
    public string Option { get; set; }

    public int OrderNo { get; set; }

    public virtual ChoiceField ChoiceField { get; set; }
}

...

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {

        modelBuilder.Entity<Form>();
        var eFormField = modelBuilder.Entity<FormField>();

        var eChoiceField = modelBuilder.Entity<ChoiceField>();

    // here I add a second Key for ChoiceField table
        eChoiceField.HasKey(cf => new { cf.Id });

        var eChoiceFieldOption = modelBuilder.Entity<ChoiceFieldOption>();

        modelBuilder.Entity<AddressField>();
        modelBuilder.Entity<DateTimeField>();
    // ...
    }