Compatibility Issues with DataClient Libraries After Migrating to .NET 6 from .NET Framework 4.8

Nova, Sirene R 0 Reputation points
2024-04-17T18:21:32.6033333+00:00

I'm reaching out for insights into an issue I've encountered after upgrading a set of C# projects from .NET Framework 4.8 to .NET 6.0. The setup consists of a primary C# calculator application, a C# bridge for external integration, and a C++/CLI wrapper. The C# bridge utilizes a dataclient package (System.Data.SqlClient in .NET Framework 4.8, Microsoft.Data.SqlClient in .NET 6.0) to import data from a SQL database.

The challenge arises with the C++/CLI wrapper when it calls into the C# bridge that depends on either the System.Data.SqlClient or Microsoft.Data.SqlClient package. This call fails specifically when the database client library is used. Without these packages, the call works correctly.

The exception thrown is as follows:

Managed exception caught in build_calculator: Could not load file or assembly 'Microsoft.Data.SqlClient, Version=5.0.0.0, Culture=neutral, PublicKeyToken=23ec7fc2d6eaa4a5'. The system cannot find the file specified.

Note that the actual version is 5.2.0.0, but the error references an incorrect version number, which seems to be the assemblyVersion specified in the .deps.json file.

Here is a summary of the steps I have taken to resolve it:

  1. Upgraded DataClient: Upgraded System.Data.SqlClient. Then later transitioned from System.Data.SqlClient to Microsoft.Data.SqlClient for better compatibility with .NET 6.0.
  2. Removed Database Connection: Verified that the C++/CLI wrapper successfully calls the C# bridge when the dataclient library is not invoked.
  3. App.config Binding Redirects: Implemented binding redirects in an attempt to resolve version conflicts. The redirects had no discernible effect, even though the configuration files were generated properly. Attempted this with both System.Data.SqlClient and Microsoft.Data.SqlClient.
  4. Direct DLL References: Extracted the DLLs and dependencies directly from the NuGet packages and referenced them in the project, which led to various other issues. Attempted this with both System.Data.SqlClient and Microsoft.Data.SqlClient.
  5. Explicit Assembly Loading: Tried to load the assembly explicitly within the C++/CLI project using Assembly::Load, ensuring the correct package version was specified. However, the correct file could not be located, and the error persisted. Attempted this with both System.Data.SqlClient and Microsoft.Data.SqlClient.

In the 5th/last step above the correct package version was searched, but still couldn't be found. Here is the exception thrown for Microsoft.Data.SqlClient:

Error loading assembly: Could not load file or assembly 'Microsoft.Data.SqlClient, Version=5.2.0.0, Culture=neutral, PublicKeyToken=23ec7fc2d6eaa4a5'. The system cannot find the file specified.

I'm hoping someone in the community, or at Microsoft, could shed some light on this problem. Has anyone else faced a similar issue? Are there known workarounds or solutions to ensure that the correct versions of the SQL client libraries are found and loaded when using a C++/CLI project in .NET 6.0?

I'm also open to suggestions for additional information I should include in this inquiry or further steps I might take to debug or resolve this issue.

Here is a section of the .deps.json file:

"Microsoft.Data.SqlClient/5.2.0": {
        "dependencies": {
          "Azure.Identity": "1.10.3",
          "Microsoft.Data.SqlClient.SNI.runtime": "5.2.0",
          "Microsoft.Identity.Client": "4.56.0",
          "Microsoft.IdentityModel.JsonWebTokens": "6.35.0",
          "Microsoft.IdentityModel.Protocols.OpenIdConnect": "6.35.0",
          "Microsoft.SqlServer.Server": "1.0.0",
          "System.Configuration.ConfigurationManager": "6.0.1",
          "System.Runtime.Caching": "6.0.0"
        },
        "runtime": {
          "lib/net6.0/Microsoft.Data.SqlClient.dll": {
            "assemblyVersion": "5.0.0.0",
            "fileVersion": "5.20.24059.2"
          }

Here is the section of cli code throwing exceptions:

#include "stdafx.h"
#include "CLIWrapper.h"
#include <msclr/marshal.h>
using namespace CSharpProjBridge;
using namespace msclr::interop;
using namespace System;
#include <cliext/vector>
#include <msclr\auto_gcroot.h>
using namespace System::Reflection;
 
namespace CLIWrapper {
	bool Wrapper::call_calculator(double* l_, double* s_, double temperature, int arraySize)
	{
		try
		{
			// Dynamically load the assembly
			Assembly^ myAssembly = Assembly::Load("Microsoft.Data.SqlClient, Version=5.2.0.0, Culture=neutral, PublicKeyToken=23ec7fc2d6eaa4a5");
			Console::WriteLine("Assembly loaded successfully.");
 
			// Rest of your code here
		}
		catch (Exception^ e)
		{
			Console::WriteLine("Error loading assembly: " + e->Message);
		}
 
		try {
			array<double>^ l_Array = gcnew array<double>(arraySize);
			array<double>^ s_Array = gcnew array<double>(arraySize);
			// Copies C++ pointer arrays to useable form for CSharpProjInterface
			Runtime::InteropServices::Marshal::Copy(IntPtr((void*)l_), l_Array, 0, arraySize);
			Runtime::InteropServices::Marshal::Copy(IntPtr((void*)s_), s_Array, 0, arraySize);
			bool success = CSharpProjInterface::CallCalculator(l_Array, s_Array, temperature);
			// Copies results from CSharpProj back to C++ pointer arrays
			Runtime::InteropServices::Marshal::Copy(l_Array, 0, IntPtr((void*)l_), 191);
			Runtime::InteropServices::Marshal::Copy(s_Array, 0, IntPtr((void*)s_), 191);
			return success;
		}
		catch (System::Exception^ ex) {
			System::Console::WriteLine("Managed exception caught in call_calculator: {0}", ex->Message);
			// Handle the exception or rethrow to propagate the error
		}
	}
 
	void Wrapper::build_calculator(char** components, char* parameterSet, int arraySize)
	{
		try
		{
			// Dynamically load the assembly
			Assembly^ myAssembly = Assembly::Load("Microsoft.Data.SqlClient, Version=5.2.0.0, Culture=neutral, PublicKeyToken=23ec7fc2d6eaa4a5");
			Console::WriteLine("Assembly loaded successfully.");
 
			// Rest of your code here
		}
		catch (Exception^ e)
		{
			Console::WriteLine("Error loading assembly: " + e->Message);
		}
 
		try {
			// Assuming BuildCalculator is a method within your managed code
			array<String^>^ compArray = gcnew array<String^, 1>(arraySize);
			for (int i = 0; i < arraySize; i++)
				// Copies components array from C++ pointer to usable form for CSharpProjInterface
				compArray[i] = marshal_as<String^>(components[i]);
			String^ dataSet = marshal_as<String^>(parameterSet);
			CSharpProjInterface::BuildCalculator(compArray, dataSet);
		}
		catch (System::Exception^ ex) {
			System::Console::WriteLine("Managed exception caught in build_calculator: {0}", ex->Message);
			// Handle the exception or rethrow to propagate the error
		}
 
	}
}
.NET
.NET
Microsoft Technologies based on the .NET software framework.
3,904 questions
SQL Server
SQL Server
A family of Microsoft relational database management and analysis systems for e-commerce, line-of-business, and data warehousing solutions.
13,947 questions
.NET CLI
.NET CLI
A cross-platform toolchain for developing, building, running, and publishing .NET applications.
337 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,995 questions
C++
C++
A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.
3,754 questions
0 comments No comments
{count} votes

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.