question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

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 issue

Reverse 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:closed
  • Created 4 years ago
  • Comments:5 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
bricelamcommented, Oct 1, 2019

Hmm, it’s possible, but a whole lot uglier than just using the string. It’s a balancing act between usefulness and cosmetics.

0reactions
conficientcommented, Oct 24, 2019

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 InverseProperty attributes to prefix the namespace, e.g.

Find: InverseProperty(nameof( and replace with InverseProperty(nameof([contextnamespace].

I’d suggest (a) fully qualifying the InverseProperty atttribute and also (b) add a switch to the scaffold to permit using the old behaviour of using a quoted string?

Read more comments on GitHub >

github_iconTop Results From Across the Web

The term 'scaffold-dbcontext' is not recognized as the name ...
The term 'scaffold-dbcontext' is not recognized as the name of a cmdlet, function, script file, or operable program · 3. Make sure that ......
Read more >
Scaffolding (Reverse Engineering) - EF Core
Reverse engineering is the process of scaffolding entity type classes and a DbContext class based on a database schema. It can be performed ......
Read more >
Breaking changes in EF Core 7.0 (EF7)
Old behavior. In EF Core 6.0, when a new entity is tracked either from a tracking query or by attaching it to the...
Read more >
Entity Framework Core 5 - Pitfalls To Avoid and Ideas to Try
There are drawbacks to scaffolding that we can avoid by starting with a Code-First approach. We can define logical relationships in C# that...
Read more >
7.1 Entity Framework 6 Support
This chapter describes how to configure and use the EF6 features that are implemented in ... Net Framework Data Provider for MySQL" type="MySql.Data....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found