OpenRIAServices / OpenRiaServices

The Open RIA Services project continues what was previously known as WCF RIA Services.
https://openriaservices.gitbook.io/openriaservices/
Apache License 2.0
54 stars 47 forks source link

Can't generate client code when using EF Code first and server connects to the database using System.Data.SqlClient #443

Closed KumG closed 8 months ago

KumG commented 9 months ago

Describe the bug

C:\Users\kumg.nuget\packages\openriaservices.client.codegen\5.4.0\build\OpenRiaServices.Client.CodeGen.targets(341,5): error : Failed to get the MetadataWorkspace for the DbContext type 'Data.MyContext'. Exception: Exception has been thrown by the target of an invocation. Exception: The type initializer for 'System.Data.SqlClient.TdsParser' threw an exception. Exception: The type initializer for 'System.Data.SqlClient.SNILoadHandle' threw an exception. Exception: Unable to load DLL 'sni.dll' or one of its dependencies: The specified module could not be found. (0x8007007E)

I also tried to connect to an Oracle database, but I got the error instead... :

Failed to get the MetadataWorkspace for the DbContext type 'Data.MyContext'. Exception: Exception has been thrown by the target of an invocation. Exception: Could not load file or assembly 'System.Diagnostics.PerformanceCounter, Version=4.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'. Reference assemblies cannot be loaded for execution. (0x80131058) Exception: Cannot load a reference assembly for execution. 41

I know it's not directly linked to OpenRIAServices, but is there a way to debug the code generation and find where it is looking for this DLL ?

Expected behavior The client code should be generated.

Details

SandstromErik commented 9 months ago

Unfortunately I am not familiar with any way of debuging the code generation as you describe. Could you provide a small sample project with this issue which I can look more into?

Daniel-Svensson commented 9 months ago

Can you check if https://github.com/OpenRIAServices/OpenRiaServices/issues/433#issuecomment-1726541064 helps with your issue.

As for debugging the code gen the error message for code generation might include a link to a command line parameter file (in temp folder), which can be used to run the code generation with the same inputs (except maybe logging parameter needs to change)

KumG commented 9 months ago

Can you check if #433 (comment) helps with your issue.

As for debugging the code gen the error message for code generation might include a link to a command line parameter file (in temp folder), which can be used to run the code generation with the same inputs (except maybe logging parameter needs to change)

Thanks!

I have a .NET 7 "MyProject.Web" project, a .NET 7 "Myproject.Data" project with all the EF6 classes, and a "Client.Data.CodeGen" project.

First, I had to add DbProviderFactories.RegisterFactory("System.Data.SqlClient", System.Data.SqlClient.SqlClientFactory.Instance); in my IDbConnectionFactory, otherwise I had the same error:

Failed to get the MetadataWorkspace for the DbContext type 'Data.MyContext'. Exception: Exception has been thrown by the target of an invocation. Exception: The specified invariant name 'System.Data.SqlClient' wasn't found in the list of registered .NET Data Providers. Client.Data.CodeGen C:\Users\kumg.nuget\packages\openriaservices.client.codegen\5.4.0\build\OpenRiaServices.Client.CodeGen.targets 341

But now, as I said, I still get the error Unable to load DLL 'sni.dll' or one of its dependencies...

I tried to add <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> but I still get the same error. In which project do I have to add this ?

Then, I removed the migrations during the database initialization, to avoid a database connection during the code generation, but now I have this error :

The code generator 'OpenRiaServices.Tools.CSharpCodeDomClientCodeGenerator' encountered a fatal exception and could not generate code for project 'C:\Sources\myProject\Client.Data.CodeGen.csproj': Object reference not set to an instance of an object. Client.Data.CodeGen C:\Users\kumg.nuget\packages\openriaservices.client.codegen\5.4.0\build\OpenRiaServices.Client.CodeGen.targets 341

I don't have any detail about the exception except "Object reference not set to an instance of an object"... Where can I find more details ?

Daniel-Svensson commented 9 months ago

<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> should be added to server project that code generation runs again (it should not be needed if server project is the. Exe web application, since it should be default true)

For more/better error messages you might want to increase log verbosity in visual studio to info (or search for and use msbuild structured log viewer)

First, I had to add DbProviderFactories.RegisterFactory("System.Data.SqlClient", System.Data.SqlClient.SqlClientFactory.Instance);

That is a bit surprising, i believ3d it would have been the default setting for ef6.

find where it is looking for this DLL ? It uses the ".deps.json" file to find references (from bin directory and dotnet sdk installation)

Can you provide a minimal same project? (If you want you can base it on aspnetcore sample project and create a new folder in the samples repository)

KumG commented 9 months ago

Thank you. I will try to reproduce the problem in a test project. I was able to generate client code by disabling the migration and commenting some code in my DomainServices but I'm still struggling with some classes. (still getting Object reference not set to an instance of an object.... )

KumG commented 9 months ago

Removing this from the DbConfiguration class allowed to generate the client without connecting to the database.

SetDatabaseInitializer(MyContextInitializer);

Instead, I set it in my Program.cs :

Database.SetInitializer(new MyContextInitializer());

The second NullReferenceException was due to some validation attributes using resources :

[RegularExpression(@"^[^*<>?""\[\]\/\\]+$", ErrorMessageResourceName = "ReportNameFormatMustBeValid", ErrorMessageResourceType = typeof(ValidationMessages))]
public string Text { get; set; }

I still don't know what the problem is but I didn't investigate yet...

AspNetCore.Hosting.AspNetCore.zip

Daniel-Svensson commented 8 months ago

It looks like the code generation logs an error and therefor does not emit any files (but still reports that it "succeeds", which might sound strange but is how it worked before .NET). However It seems to be missing more useful information such as stack trace.

If you want to investigate further you can compile the codegen yourself (I don't think I can do more in the near time)

  1. An easy approach to debug the code gen is to add a "Debugger.Launch()" at the start of "OpenRiaServices.Tools.CodeGenTask" image

  2. Build it in and OpenRiaServices.Tools in debug mode and copy both the .dll and the .pdb file to %USERPROFILE%:\.nuget\packages\openriaservices.client.codegen\5.4.0\tasks\net6.0 (or correct folder where your .Nuget package cache is located)

  3. Build the solution (preferable from command line or msbuild structure log viewer) you should now get a dialog allowing you to attach the debugger

As for DBContext

Removing this from the DbConfiguration class allowed to generate the client without connecting to the database.

SetDatabaseInitializer(MyContextInitializer);

Instead, I set it in my Program.cs :

Database.SetInitializer(new MyContextInitializer());

Great that you solved it.

2-. I think the code for entityframework internally uses Database.SetInitializer(null); in some place to avoid this problem, but is obviously does not work if the DbConfiguration class does the setup (I am 75% sure it works in static constructor as I have seen some MS examples).

If you have any idea of making it work for your case as well maybe with calling SetInitializer(null) twice then feel free to suggest a fix.

Daniel-Svensson commented 8 months ago

FYI @SandstromErik , maybe something similar to above should be added to the wiki (there is a page for debugging the code generation)

Daniel-Svensson commented 8 months ago

@KumG does it work if you add the openriaservices.t4 nuget to the server project ? (I have seen a large working solution using resource files as in your example using the t4 codegen, so it might be a possible workaround)

Daniel-Svensson commented 8 months ago

5.4.1 is now released with the fix for regex attributes