问题
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