简体   繁体   English

如何使用 Fluent API 和 Code First Migrations 设置外键名称?

[英]How can I set the foreign key name using the Fluent API with Code First Migrations?

I'm trying to set the foreign key name (not the foreign key column) using Code First Migrations on EF6.4.我正在尝试在 EF6.4 上使用 Code First Migrations 设置外键名称(不是外键列)。

I know that it can be set by updating the generated migration code, like so:我知道可以通过更新生成的迁移代码来设置它,如下所示:

.ForeignKey("Documents", Function(t) t.DocumentId, cascadeDelete:=True, name:="FK_Sections_Documents")

...but I'd like to do it before the migration is added, using the Fluent API. ...但我想在添加迁移之前使用 Fluent API 执行此操作。

I seem to recall something about the HasForeignKey() call accepting a Func that contains a call to an anonymous type in its body, such as what we find here .我似乎想起了一些关于HasForeignKey()调用接受一个Func事情,该HasForeignKey()在其主体中包含对匿名类型的调用,例如我们在这里找到的内容。 But I'll be darned if I can locate anything discussing what the general structure of that type should be.但是如果我能找到任何讨论那种类型的一般结构应该是什么的东西,我会被诅咒的。

The official documentation doesn't discuss it:官方文档没有讨论它:

Nor do these similar Q&As quite exactly address the issue:这些类似的问答也没有完全解决这个问题:

This same question was asked a couple of months ago here , but so far it hasn't received an answer.几个月前在这里提出了同样的问题,但到目前为止还没有得到答复。

I'm using EntityTypeConfiguration(Of T) .我正在使用EntityTypeConfiguration(Of T) Here's my code:这是我的代码:

Namespace Configuration
  Friend Class SectionConfig
    Inherits EntityTypeConfiguration(Of Db.Section)

    Public Sub New()
      Me.HasRequired(Function(Section) Section.Document).WithMany.HasForeignKey(Function(Section) Section.DocumentId)

      Me.Property(Function(Section) Section.DocumentId).IsRequired()
      Me.Property(Function(Section) Section.SectionId).IsRequired()
      Me.Property(Function(Section) Section.IsSent).IsRequired()
      Me.Property(Function(Section) Section.Markup).IsRequired.IsMaxLength()
      Me.Property(Function(Section) Section.Title).IsRequired.HasMaxLength(60)

      Me.HasIndex(Function(Section) Section.DocumentId).HasName("IX_Sections_DocumentId")
      Me.HasIndex(Function(Section) Section.SectionId).HasName("IX_Sections_SectionId")
      Me.HasIndex(Function(Section) Section.Title).HasName("IX_Sections_Title")

      Me.Ignore(Function(Section) Section.Subject)
    End Sub
  End Class
End Namespace

How does one set a foreign key name, or—even more specific, assuming I'm remembering correctly—what should be the general structure of that anonymous type?如何设置外键名称,或者——更具体地说,假设我没记错——匿名类型的一般结构应该是什么?

--UPDATE-- - 更新 -

I tried this:我试过这个:

Me.HasRequired(Function(Section) Section.Document).WithMany.HasForeignKey(Function(Section) New With {.DependentKeyExpression = Section.DocumentId, .Name = "FK_Sections_Documents"})

...but a migration creation attempt answered with this: ...但是一个迁移创建尝试回答了这个:

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. System.Reflection.TargetInvocationException:调用的目标已抛出异常。 ---> System.InvalidOperationException: The properties expression 'Section => new VB$AnonymousType_0`2(DependentKeyExpression = Section.DocumentId, Name = "FK_Sections_Documents")' is not valid. ---> System.InvalidOperationException: 属性表达式 'Section => new VB$AnonymousType_0`2(DependentKeyExpression = Section.DocumentId, Name = "FK_Sections_Documents")' 无效。 The expression should represent a property: C#: 't => t.MyProperty' VB.Net: 'Function(t) t.MyProperty'.该表达式应表示一个属性:C#: 't => t.MyProperty' VB.Net: 'Function(t) t.MyProperty'。 When specifying multiple properties use an anonymous type: C#: 't => new { t.MyProperty1, t.MyProperty2 }' VB.Net: 'Function(t) New With { t.MyProperty1, t.MyProperty2 }'.当指定多个属性时使用匿名类型:C#: 't => new { t.MyProperty1, t.MyProperty2 }' VB.Net: 'Function(t) New With { t.MyProperty1, t.MyProperty2 }'。

So, given that the anonymous type construct is for specifying the key column(s), it's not the way to specify a foreign key name.因此,鉴于匿名类型构造用于指定键列,它不是指定外键名称的方法。

The question still stands: How may we specify the foreign key name using the Fluent API in EF6.4?问题仍然存在:我们如何使用 EF6.4 中的 Fluent API 指定外键名称?

Unfortunately, in EF6 there is no built-in way to do this.不幸的是,在 EF6 中没有内置的方法来做到这一点。 To clarify your question a little, What you are asking is how to specify the name of the underlying database constraint that is created based on the foreign key specified in your model.为了稍微澄清您的问题,您要问的是如何指定基于模型中指定的外键创建的底层数据库约束的名称。 This would be a job for the migration generator.这将是迁移生成器的工作。 Though the functionality is there in EF6 migrations to pass a constraint name, If you explore the source code you'll see it is never actually used, and all of the methods are private and internal.尽管 EF6 迁移中存在传递约束名称的功能,但如果您查看源代码,您会发现它从未被实际使用过,并且所有方法都是私有的和内部的。

This was recitfied in EFCore where you can now do: Me.HasRequired(Function(Section) Section.Document).WithMany().HasForeignKey(Function(Section) Section.DocumentId).HasConstraintName("FK_Sections_Documents") but that doesn't help you.这在 EFCore 中被引用,你现在可以这样做: Me.HasRequired(Function(Section) Section.Document).WithMany().HasForeignKey(Function(Section) Section.DocumentId).HasConstraintName("FK_Sections_Documents")但这不是帮你。

So what you need is to write a custom migration generator that will do what you want.因此,您需要编写一个自定义迁移生成器来执行您想要的操作。 This exact question was answered (code in C#, database is SQL Server) in this post: Change foreign key constraint naming convention在这篇文章中回答了这个确切的问题(C# 中的代码,数据库是 SQL Server): 更改外键约束命名约定

Using Fluent Mappings, ForeignKey attibute expects a property name in your class as the argument.使用 Fluent Mappings,ForeignKey 属性需要类中的属性名称作为参数。

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
  modelBuilder.Entity<WidgetEntity>()
   .HasRequired(w => w.Sequence)
   .WithMany()
   .Map(m => m.MapKey("FK_Sections_Documents"));
}

LookupData Config:查找数据配置:

HasRequired(p => p.LookupType).WithMany(p=>p.LookupData).HasForeignKey(p=>p.LookupTypeId).WillCascadeOnDelete(false);

Name Config:名称配置:

HasOptional(p => p.Gender).WithMany(p=>p.Name).HasForeignKey(p=>p.GenderLookupId).WillCascadeOnDelete(false);

You can also use the InversePropertyAttribute , if you don't want to use fluent syntax, as the Inverse Property is easier to read and are written just above the property they are affecting.如果您不想使用流畅的语法,您也可以使用InversePropertyAttribute ,因为 Inverse 属性更易于阅读并且写在它们影响的属性上方。

Update:更新:

Use ForeignKey with an associated property将 ForeignKey 与关联的属性一起使用

Example 1:示例 1:

[Table("ENTITIES")]
public class Entity {

    [Column("ENTITY_NO")]
    public int No { set; get; }

    [Column("SEQUENCE_NO")]
    public int SequenceNo { set; get; }

    [ForeignKey("SequenceNo")] //Has to be a property name, not table column name
    public Sequence Sequence { set; get; }

    // and other properties that map correctly
}

[Table("SEQUENCES")]
public class Sequence { 

    [Column("SEQUENCE_NO")]
    public int No { set; get; }

    [Column("NUMBER")]
    public int Number { set; get; }
}

Example 2:示例 2:

[Table("ENTITIES")]
public class Entity {

    [Column("ENTITY_NO")]
    public int No { set; get; }

    [ForeignKey("Sequence")] //Has to be a property name, not table column name
    [Column("SEQUENCE_NO")]
    public int SequenceNo { set; get; }

    public Sequence Sequence { set; get; }

    // and other properties that map correctly
}

[Table("SEQUENCES")]
public class Sequence { 

    [Column("SEQUENCE_NO")]
    public int No { set; get; }

    [Column("NUMBER")]
    public int Number { set; get; }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM