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

爱⌒轻易说出口 提交于 2020-01-11 08:01:12

问题


I'm trying to set the foreign key name (not the foreign key column) using Code First Migrations on EF6.4.

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.

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. 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:

  • Fluent API with VB.NET
  • HasForeignKey(Of TKey)

Nor do these similar Q&As quite exactly address the issue:

  • EF Code First Fluent API specifying the Foreign Key property
  • Entity Framework Code First Mapping Foreign Key Using Fluent API
  • Specifying Foreign Key Entity Framework Code First, Fluent Api

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). 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.InvalidOperationException: The properties expression 'Section => new VB$AnonymousType_0`2(DependentKeyExpression = Section.DocumentId, Name = "FK_Sections_Documents")' is not valid. The expression should represent a property: 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 }'.

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?


回答1:


Unfortunately, in EF6 there is no built-in way to do this. 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.

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.

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




回答2:


Using Fluent Mappings, ForeignKey attibute expects a property name in your class as the argument.

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.

Update:

Use ForeignKey with an associated property

Example 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:

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


来源:https://stackoverflow.com/questions/59363602/how-can-i-set-the-foreign-key-name-using-the-fluent-api-with-code-first-migratio

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!