Scaffold-DbContext Is Generating Invalid Code When A Property With The Same Name As The Type Exists and Two Properties Use That Type
See original GitHub issueReverse engineering a SQL Server database using DataAnnotations will result in invalid code generated when a child table contains multiple foreign keys to the same parent table if one of the foreign keys will result in a member being created in the child entity with the same name as the generated parent type.
Steps to reproduce
Create a database with two tables:
CREATE TABLE [dbo].[Child](
[Id] [int] IDENTITY(1,1) NOT NULL,
[ParentId] [int] NOT NULL,
[AnotherParentId] [int] NOT NULL,
CONSTRAINT [PK_Child] PRIMARY KEY CLUSTERED
(
[Id] ASC
)
GO
CREATE TABLE [dbo].[Parent](
[Id] [int] IDENTITY(1,1) NOT NULL,
CONSTRAINT [PK_Parent] PRIMARY KEY CLUSTERED
(
[Id] ASC
)
GO
ALTER TABLE [dbo].[Child] WITH CHECK ADD CONSTRAINT [FK_Child_Parent] FOREIGN KEY([ParentId])
REFERENCES [dbo].[Parent] ([Id])
GO
ALTER TABLE [dbo].[Child] CHECK CONSTRAINT [FK_Child_Parent]
GO
ALTER TABLE [dbo].[Child] WITH CHECK ADD CONSTRAINT [FK_Child_Parent1] FOREIGN KEY([AnotherParentId])
REFERENCES [dbo].[Parent] ([Id])
GO
ALTER TABLE [dbo].[Child] CHECK CONSTRAINT [FK_Child_Parent1]
GO
Scaffold the tables:
Scaffold-DbContext "Server=localhost;Database=EFScaffoldRepro;Integrated Security=true;MultipleActiveResultSets=true" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Data -DataAnnotation -Force
PM> Scaffold-DbContext "Server=localhost;Database=EFScaffoldRepro;Integrated Security=true;MultipleActiveResultSets=true" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Data -DataAnnotation -Force -verbose
Using project 'EFScaffoldRepro'.
Using startup project 'EFScaffoldRepro'.
Build started...
Build succeeded.
C:\Program Files\dotnet\dotnet.exe exec --depsfile C:\Users\sblomfield\Source\Repos\EFScaffoldBug\EFScaffoldRepro\bin\Debug\netcoreapp3.0\EFScaffoldRepro.deps.json --additionalprobingpath C:\Users\sblomfield\.nuget\packages --additionalprobingpath C:\Microsoft\Xamarin\NuGet --additionalprobingpath "C:\Program Files\dotnet\sdk\NuGetFallbackFolder" --runtimeconfig C:\Users\sblomfield\Source\Repos\EFScaffoldBug\EFScaffoldRepro\bin\Debug\netcoreapp3.0\EFScaffoldRepro.runtimeconfig.json C:\Users\sblomfield\.nuget\packages\microsoft.entityframeworkcore.tools\3.0.0-preview9.19423.6\tools\netcoreapp2.0\any\ef.dll dbcontext scaffold "Server=localhost;Database=EFScaffoldRepro;Integrated Security=true;MultipleActiveResultSets=true" Microsoft.EntityFrameworkCore.SqlServer --json --output-dir Data --data-annotations --force --verbose --no-color --prefix-output --assembly C:\Users\sblomfield\Source\Repos\EFScaffoldBug\EFScaffoldRepro\bin\Debug\netcoreapp3.0\EFScaffoldRepro.dll --startup-assembly C:\Users\sblomfield\Source\Repos\EFScaffoldBug\EFScaffoldRepro\bin\Debug\netcoreapp3.0\EFScaffoldRepro.dll --project-dir C:\Users\sblomfield\Source\Repos\EFScaffoldBug\EFScaffoldRepro\ --language C# --working-dir C:\Users\sblomfield\Source\Repos\EFScaffoldBug --root-namespace EFScaffoldRepro
Using assembly 'EFScaffoldRepro'.
Using startup assembly 'EFScaffoldRepro'.
Using application base 'C:\Users\sblomfield\Source\Repos\EFScaffoldBug\EFScaffoldRepro\bin\Debug\netcoreapp3.0'.
Using working directory 'C:\Users\sblomfield\Source\Repos\EFScaffoldBug\EFScaffoldRepro'.
Using root namespace 'EFScaffoldRepro'.
Using project directory 'C:\Users\sblomfield\Source\Repos\EFScaffoldBug\EFScaffoldRepro\'.
Finding design-time services for provider 'Microsoft.EntityFrameworkCore.SqlServer'...
Using design-time services from provider 'Microsoft.EntityFrameworkCore.SqlServer'.
Finding design-time services referenced by assembly 'EFScaffoldRepro'.
No referenced design-time services were found.
Finding IDesignTimeServices implementations in assembly 'EFScaffoldRepro'...
No design-time services were found.
Found default schema dbo.
Found type alias with name: sys.sysname which maps to underlying data type nvarchar(128).
Found table with name: dbo.Child.
Found table with name: dbo.Parent.
Found column with table: dbo.Child, column name: Id, ordinal: 1, data type: sys.int, maximum length: 4, precision: 10, scale: 0, nullable: False, identity: True, default value: (null), computed value: (null)
Found column with table: dbo.Child, column name: ParentId, ordinal: 2, data type: sys.int, maximum length: 4, precision: 10, scale: 0, nullable: False, identity: False, default value: (null), computed value: (null)
Found column with table: dbo.Child, column name: AnotherParentId, ordinal: 3, data type: sys.int, maximum length: 4, precision: 10, scale: 0, nullable: False, identity: False, default value: (null), computed value: (null)
Found column with table: dbo.Parent, column name: Id, ordinal: 1, data type: sys.int, maximum length: 4, precision: 10, scale: 0, nullable: False, identity: True, default value: (null), computed value: (null)
Found primary key with name: PK_Child, table: dbo.Child.
Found primary key with name: PK_Parent, table: dbo.Parent.
Found foreign key on table: FK_Child_Parent, name: dbo.Child, principal table: dbo.Parent, delete action: NO_ACTION.
Found foreign key on table: FK_Child_Parent1, name: dbo.Child, principal table: dbo.Parent, delete action: NO_ACTION.
PM>
Invalid code will be generated in the new entity Child:
public partial class Child
{
[Key]
public int Id { get; set; }
public int ParentId { get; set; }
public int AnotherParentId { get; set; }
[ForeignKey(nameof(AnotherParentId))]
[InverseProperty(nameof(Parent.ChildAnotherParent))]
public virtual Parent AnotherParent { get; set; }
[ForeignKey(nameof(ParentId))]
[InverseProperty("ChildParent")]
public virtual Parent Parent { get; set; }
}
The InverseProperty for Parent.ChildAnotherParent will not compile.
Code Sample Attached: EFScaffoldBug.zip
Solution
To solve, nameof() needs to use fully qualified names instead of only the entity class name when the class the attribute will be added in has a member with the same name as the type the attribute will reference.
Alternately, nameof() in InverseProperty attributes generated could always use the fully qualified name of the type.
Modify in EFCore.Design/Scaffolding/Internal/CSharpEntityTypeGenerator:368
Further technical details
EF Core version: 3.0.0-preview9.19423.6 Database provider: Microsoft.EntityFrameworkCore.SqlServer Target framework: .NET Core 3.0 Operating system: Windows 10 Pro Version 1809 IDE: Visual Studio Community 2019 Preview - 16.3.0 Preview 3.0
Issue Analytics
- State:
- Created 4 years ago
- Comments:5 (2 by maintainers)

Top Related StackOverflow Question
Hmm, it’s possible, but a whole lot uglier than just using the string. It’s a balancing act between usefulness and cosmetics.
I just ran across this bug in EF core tools v3.0.0 as well. I understand the logic behind using
nameof(but since this is generated code it may be an optimisation too far?My expectation is that I should never have to manually edit or rename any of the generated classes so the code should be able to use a string value, which then avoids this name conflict.
The short-term fix is to do a search-and-replace on the
InversePropertyattributes to prefix the namespace, e.g.Find:
InverseProperty(nameof(and replace withInverseProperty(nameof([contextnamespace].I’d suggest (a) fully qualifying the
InversePropertyatttribute and also (b) add a switch to the scaffold to permit using the old behaviour of using a quoted string?