Cannot define composite PK with Fluent API in VB.N

2019-08-04 13:35发布

问题:

I am trying to define a composite PK for a class where one of the columns is a FK to another table. The code compiles without any errors but when I try to migrate the changes I get the following error

PM> Update-Database -Force -TargetMigration:0
Specify the '-Verbose' flag to view the SQL statements being applied to the target database.
System.InvalidOperationException: The properties expression 'x => new VB$AnonymousType_0`2(RtepNumber = x.RtepNumber, ContractId = x.Contract.ContractId)' 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 }'.

The properties expression 'x => new VB$AnonymousType_0`2(RtepNumber = x.RtepNumber, ContractId = x.Contract.ContractId)' 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 }'.

However, the that is supposedly causing the exception does seem to be in the syntax that is requested.

Given the following classes

Public Class ParentClass
   Public Property ParentClassId as String

   Public Property Title As String

   Public Property ChildClasses As ICollection(Of ChildClass)
End Class

Public Class ChildClass      
   Public Property ParentClass As ParentClass

   Public Property ChildClassId As String

   Public Property Title As String
End Class

And the following Fluent API code

Protected Overrides Sub OnModelCreating(ByVal modelBuilder As System.Data.Entity.DbModelBuilder)
   MyBase.OnModelCreating(modelBuilder)

   modelBuilder.Entity(Of Models.ChildClass).HasRequired(Function(x) x.ParentClass).WithMany(Function(x) x.ChildClasses).Map(Function(x) x.MapKey("ContractId"))

   modelBuilder.Entity(Of Models.ChildClass).HasKey(Function(x) New With {x.ParentClass.ParentClassId, x.ChildClassId})
End Sub

The equivalent SQL code would be

CREATE TABLE [dbo].[ParentClass](
    [ParentClassId] [nvarchar](10) NOT NULL,
    [Title] [nvarchar](25) NOT NULL,
 CONSTRAINT [PK_ParentClass] PRIMARY KEY CLUSTERED 
(
    [ParentClassId] ASC
)
)
GO

CREATE TABLE [dbo].[ChildClass](
    [ParentClassId] [nvarchar](10) NOT NULL,
    [ChildClassId] [nvarchar](10) NOT NULL,
    [Title] [nvarchar](25) NOT NULL,
 CONSTRAINT [PK_ChildClass] PRIMARY KEY CLUSTERED 
(
    [ParentClassId] ASC,
    [ChildClassId] ASC
)
)
GO

ALTER TABLE [dbo].[ChildClass]  WITH CHECK ADD  CONSTRAINT [FK_ChildClass_ParentClass] FOREIGN     KEY([ParentClassId])
REFERENCES [dbo].[ParentClass] ([ParentClassId])
GO

ALTER TABLE [dbo].[ChildClass] CHECK CONSTRAINT [FK_ChildClass_ParentClass]
GO

Thank you for any help you can provide.

Edit:

After more research (i.e. experimenting) the problem seems to stem from the use of the navigation property in the PK definition. So changing the code to look like

Public Class ChildClass      
   Public Property ParentClassId As String

   Public Property ChildClassId As String

   Public Property Title As String
End Class

Protected Overrides Sub OnModelCreating(ByVal modelBuilder As System.Data.Entity.DbModelBuilder)
   MyBase.OnModelCreating(modelBuilder)

   modelBuilder.Entity(Of Models.ChildClass).HasKey(Function(x) New With {x.ParentClassId, x.ChildClassId})
End Sub

clears the Exception but, of course, I now lost my ParentClass navigation property in ChildClass.

Still perplexed.

回答1:

You can keep the navigation property in your class...

Public Class ChildClass      
    Public Property ParentClassId As String

    Public Property ChildClassId As String

    Public Property Title As String

    Public Property ParentClass As ParentClass
End Class

...and then use HasForeignKey instead of MapKey in your mapping:

modelBuilder.Entity(Of Models.ChildClass). _
    HasKey(Function(x) New With {x.ParentClassId, x.ChildClassId})

modelBuilder.Entity(Of Models.ChildClass). _
    HasRequired(Function(x) x.ParentClass). _
    WithMany(Function(x) x.ChildClasses). _
    HasForeignKey(Function(x) x.ParentClassId)

If the foreign key has another name in the database table add a column name mapping:

modelBuilder.Entity(Of Models.ChildClass). _
    Property(Function(x) x.ParentClassId). _
    HasColumnName("ContractId")


回答2:

It seems that you have to have the scalar property in the child class. Keys can only be defined using explicit scalar properties.