ErikEJ / EFCorePowerTools

Entity Framework Core Power Tools - reverse engineering, migrations and model visualization in Visual Studio & CLI
MIT License
2.19k stars 298 forks source link

CLI is throwing Exception The key {'Id'} cannot be added to keyless type '<TableName>(Dictionary<string, object>)' although I am excluding all tables #1824

Closed xheox closed 1 year ago

xheox commented 1 year ago

I wanted only few stored procs so I marked all (tables, functions, views and SPs) with filter: "exclusionWildcard": "*" and then added ["exclude": false] to the stored procs I wanted to reverse engineer. I get the following error complaining about this table although I am excluding all tables. May be I am missing something here. I checked SQL and that table did not have a PK constraint. I could not do anything since I don't have enough permissions to Alter the table and see if this solves the issue.

System.InvalidOperationException: The key {'Id'} cannot be added to keyless type '(Dictionary<string, object>)'. at Key Microsoft.EntityFrameworkCore.Metadata.Internal.EntityType.AddKey(IReadOnlyList properties, ConfigurationSource configurationSource) at IMutableKey Microsoft.EntityFrameworkCore.Metadata.Internal.EntityType.Microsoft.EntityFrameworkCore.Metadata.IMutableEntityType.AddKey(IReadOnlyList properties) at IMutableForeignKey Microsoft.EntityFrameworkCore.Scaffolding.Internal.RelationalScaffoldingModelFactory.VisitForeignKey(ModelBuilder modelBuilder, DatabaseForeignKey foreignKey) at ModelBuilder Microsoft.EntityFrameworkCore.Scaffolding.Internal.RelationalScaffoldingModelFactory.VisitForeignKeys(ModelBuilder modelBuilder, IList foreignKeys) at ModelBuilder Microsoft.EntityFrameworkCore.Scaffolding.Internal.RelationalScaffoldingModelFactory.VisitDatabaseModel(ModelBuilder modelBuilder, DatabaseModel databaseModel) at IModel Microsoft.EntityFrameworkCore.Scaffolding.Internal.RelationalScaffoldingModelFactory.Create(DatabaseModel databaseModel, ModelReverseEngineerOptions options) at ScaffoldedModel RevEng.Core.ReverseEngineerScaffolder.ScaffoldModel(string connectionString, DatabaseModelFactoryOptions databaseOptions, ModelReverseEngineerOptions modelOptions, ModelCodeGenerationOptions codeOptions, bool removeNullableBoolDefaults, bool dbContextOnly, bool entitiesOnly, bool useSchemaFolders) in ReverseEngineerScaffolder.cs:371 at SavedModelFiles RevEng.Core.ReverseEngineerScaffolder.GenerateDbContext(ReverseEngineerCommandOptions options, List schemas, string outputContextDir, string modelNamespace, string contextNamespace , string projectPath, string outputPath) in ReverseEngineerScaffolder.cs:102 at ReverseEngineerResult RevEng.Core.ReverseEngineerRunner.GenerateFiles(ReverseEngineerCommandOptions options) in ReverseEngineerRunner.cs:81 at ReverseEngineerResult ErikEJ.EFCorePowerTools.HostedServices.ScaffoldHostedService.<>cDisplayClass6_0.b0() in ScaffoldHostedService.cs:83 at void ErikEJ.EFCorePowerTools.Services.DisplayService.<>cDisplayClass7_0`1.b0(StatusContext ctx) in DisplayService.cs:63 at Task Spectre.Console.Status.<>cDisplayClass14_0.b0(StatusContext ctx) in Status.cs:44 at void Spectre.Console.Status.<>cDisplayClass16_0.<b0>d.MoveNext() in Status.cs:79 at void Spectre.Console.Status.<>cDisplayClass17_01.<<StartAsync>b__0>d.MoveNext() in Status.cs:120 at void Spectre.Console.Progress.<>c__DisplayClass28_01.<b0>d.MoveNext() in Progress.cs:133 at async Task Spectre.Console.Internal.DefaultExclusivityMode.RunAsync(Func<Task> func) in DefaultExclusivityMode.cs:40 at async Task Spectre.Console.Progress.StartAsync(Func<ProgressContext, Task> action) in Progress.cs:116 at async Task Spectre.Console.Status.StartAsync(string status, Func<StatusContext, Task> func) in Status.cs:117 at async Task Spectre.Console.Status.StartAsync(string status, Func<StatusContext, Task> action) in Status.cs:77 at void Spectre.Console.Status.Start(string status, Action action) in Status.cs:48 at T ErikEJ.EFCorePowerTools.Services.DisplayService.Wait(string message, Func doFunc) in DisplayService.cs:58 at async Task ErikEJ.EFCorePowerTools.HostedServices.ScaffoldHostedService.ExecuteAsync(CancellationToken stoppingToken) in ScaffoldHostedService.cs:81 at async Task Microsoft.Extensions.Hosting.Internal.Host.StartAsync(CancellationToken cancellationToken) at async Task Microsoft.Extensions.Hosting.HostingAbstractionsHostBuilderExtensions.StartAsync(IHostBuilder hostBuilder, CancellationToken cancellationToken)

Provide steps to reproduce

Please include a complete description or project that I can redo/run to reproduce the issue.

Provide technical details

EF Core Power Tools 6.0.0-rc.2 and 7.0.0-rc.2

"code-generation": { "enable-on-configuring": false, "type": "all", "use-database-names": false, "use-data-annotations": false, "use-nullable-reference-types": false, "use-inflector": false, "use-legacy-inflector": false, "use-many-to-many-entity": false, "use-t4": false, "remove-defaultsql-from-bool-properties": false, "soft-delete-obsolete-files": false, "discover-multiple-stored-procedure-resultsets-preview": false, "use-alternate-stored-procedure-resultset-discovery": false }

ErikEJ commented 1 year ago

I do not see any exclusions in the fragments of the config file you Shared. Always potential for a bug though

ErikEJ commented 1 year ago

Hm. Definitely looks like your exclusions are not working. Please sha3e a complete config

xheox commented 1 year ago

Thanks @ErikEJ This is how it looks like :

{ "$schema": "https://raw.githubusercontent.com/ErikEJ/EFCorePowerTools/master/samples/efcpt-schema.json", "code-generation": { "enable-on-configuring": false, "type": "all", "use-database-names": false, "use-data-annotations": false, "use-nullable-reference-types": false, "use-inflector": false, "use-legacy-inflector": false, "use-many-to-many-entity": false, "use-t4": false, "remove-defaultsql-from-bool-properties": false, "soft-delete-obsolete-files": false, "discover-multiple-stored-procedure-resultsets-preview": false, "use-alternate-stored-procedure-resultset-discovery": false }, "names": { "root-namespace": "test efpt", "dbcontext-name": "MyContext", "dbcontext-namespace": null, "model-namespace": null }, "file-layout": { "output-path": "Models", "output-dbcontext-path": null }, "tables": [ { "exclusionWildcard": "" }, { "name": "<table 1>" }, { "name": "

" } ], "views": [ { "exclusionWildcard": "" }, { "name": "<view 1>" }, { "name": "" } ], "stored-procedures": [ { "exclusionWildcard": "" }, { "name": "wanted SP 1", "exclude": false }, { "name": "wanted SP 2", "exclude": false }, { "name": "ignored SP" }, { "name": "another ignored SP" } ], "functions": [ { "exclusionWildcard": "" }, { "name": "<func 1>" }, { "name": "" } ] }

ErikEJ commented 1 year ago

The Exclusion wildcard should be a * symbol. I cannot see that of for example tables.

xheox commented 1 year ago

I think something went wrong when I copied pasted here but my json file has the '*' for all objects like this :

"tables": [ { "exclusionWildcard": "*" },

I even ran it again to make sure. Without any luck. Object discovery is fine but it keeps failing with the same error.

ErikEJ commented 1 year ago

I will attempt a repro but might have to ask you to share the schema (privately perhaps?). Are you also excluding all views?

xheox commented 1 year ago

Thanks @ErikEJ, Yes I wanted just to get few SPs mapped with EFPT. Unfortunatelly I cannot share the DB schema.
However here the schema of the table that causes the issue : (I changed its name and couple of fields and containts) One thing to note is this table has an identity column 'ID' but without a PK constraint. The error is : The key {'Id'} cannot be added to keyless type ('Id' vs ID) Maybe that's the issue there Table schema :

CREATE TABLE [dbo].[Table1]( [ID] [int] IDENTITY(1,1) NOT NULL, [FK_ID] [int] NOT NULL, [IsInternal] [bit] NULL, [Code] varchar NULL, [Description] varchar NULL, UNIQUE NONCLUSTERED ( [ID] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO

ALTER TABLE [dbo].[Table1] ADD CONSTRAINT [DF_Table1IsInternal] DEFAULT ((1)) FOR [IsInternal] GO

ALTER TABLE [dbo].[Table1] WITH CHECK ADD CONSTRAINT [FK_Table1FeedID] FOREIGN KEY([FK_ID]) REFERENCES [dbo].[Table2] ([ID]) GO

ALTER TABLE [dbo].[Table1] CHECK CONSTRAINT [FK_Table1FeedID] GO

ErikEJ commented 1 year ago

Can you test the behaviour of the VS extension?

xheox commented 1 year ago

Unfortunatelly, I have no permissions to install the VS extension on the VM (only place from where the DB is accessible). However, if you have any idea what the extension installer is doing, I can may be try to do that manually and see

ErikEJ commented 1 year ago

Let's wait then. Are you able to try with the dotnet ef tool?

xheox commented 1 year ago

I can yes. What do you suggest ?

ErikEJ commented 1 year ago

I suggest installing and try to run "dotnet ef dbcontext scaffold"

xheox commented 1 year ago

Tried it and got the same error. Should I open an issue with EF core then ?

ErikEJ commented 1 year ago

Yes it looks like an EF Core issue then ( unlikely to get a quick fix.) I think I cannot even work around it, possibly only workaround would be to fix your actual DB schema, either removing the foreign key pointing to the table without a primary key or add a foreign key to the table.

xheox commented 1 year ago

Yes, That would be nice if I had access. Howver, why is the EFPT processing the tables although I am filtering them out all. Shouldn't it be skipping these completely ?

ErikEJ commented 1 year ago

@xheox yeah, maybe it should be skipping all tables and views, but currently it is not happening, as stored procs are not supported by the built-in scaffolding.

I think it might be an issue to skip it, as the DbContext will not be generated then. Maybe you can opt-in to scaffold a single table as a workaround by setting exclude: false for a single (dummy) table?

xheox commented 1 year ago

that did the trick. Thanks @ErikEJ.