简体   繁体   中英

.net core : load an assembly and its unmanaged dependencies at runtime

I want to be able to load a database driver assembly at runtime and perform a connection to the server.

For example in a console application I want to load the SQLite driver from my nuget package and load the type: SqliteConnection . Here my pseudo code:

string path = Path.Combine(NugetPackagesDir, @"microsoft.data.sqlite\1.1.0\lib\netstandard1.3\Microsoft.Data.Sqlite.dll");
var myAssembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(path);
var myType = myAssembly.GetType("Microsoft.Data.Sqlite.SqliteConnection");
IDbConnection cnn = (IDbConnection)Activator.CreateInstance(myType);
cnn.ConnectionString = "Data Source=:memory:";
cnn.Open();

It works until the cnn.Open() point, where it fails finding an unmanaged dependency:

System.DllNotFoundException : 'Unable to load DLL 'sqlite3'

I've got the same behavior with Sql Server and the native dependency:

System.DllNotFoundException : 'Unable to load DLL 'sni.dll'

If I copy the dll in the driver package it works, but I find the idea not convenient.


UPDATE

I do not know up-front which kinds of DbConnection I have to instantiate nor the os I am on.

The context is that my library implements Microsoft.Build.Utilities.Task and is launched by MsBuild.exe after a project build. So I have the $(TargetPath) of the application I want the DbConnection (and the driver assembly name in a config file or elsewhere).

In a .NET world all the assemblies (managed or not) are in the debug/release folder $(TargetPath) or the GAC. But in .net core I only have the deps.json file.

So my guess : use the DependencyContext class to read the deps.json file. Find the database driver it uses and copy the assembly and all its dependencies (depending on the os) in a temp folder where I finally can instantiate the DbConnection class I want.


UPDATE 2

My goal is to be able, after a build, to run the sql migration scripts of any application which use the nuget package I develop. So my code runs inside a MSBuild task, not in the target application.

After a build, the MsBuild Task loads the target project app.config file (for .NET), which should have declared some variables like the:

  • database driver used on the project
  • connection string
  • migration scripts folder(s)...

With those data I then create a DbConnection and run the scripts found in the specified folder(s).

The easiest way to do this is to let the runtime handle assembly loading automatically for you. Don't call Assembly.Load. Instead, add references to both Microsoft.Data.Sqlite and System.Data.SqlClient NuGet packages to your project. With csproj and VS 2017, this looks like:

<PackageReference Include="Microsoft.Data.Sqlite" Version="1.1.0" />
<PackageReference Include="System.Data.SqlClient" Version="4.3.0" />

If you are using project.json, it is

{
  "dependencies": {
      "Microsoft.Data.Sqlite": "1.1.0",
      "System.Data.SqlClient": "4.3.0"
  }
}

Then, add a class that switches between different implementations of DbConnection.

public class MyConnectionFactory
{
    public DbConnection Create(string serverType, string connectionString)
    {
       if (serverType == "sqlite") return new SqliteConnection(connectionString);
       else if (serverType == "sqlserver") return new SqlConnection(connectionString);

       throw new ArgumentException($"{serverType} not supported");
    }
}

Trying to implement assembly loading logic yourself will put you in a world of hurt that you don't want. Stick with using NuGet packages instead.

This is problematic, of course, if you don't know up-front which kinds of DbConnection you want. If this is your scenario, then you'll need copy all the unmanaged and managed libraries from the NuGet cache into the application base directory (location of the assembly with the Program.Main). This is not a recommend approach and won't work if you want .NET Core to work on multiple operating systems (Linux, macOS) and platforms (x86, x64, ARM).

Edit

Probably executing with unmanaged code inside MSBuild is going to be very difficult as MSBuild is controls the host and assembly loading. You will probably find a simpler solution is to start a new .NET Core process from MSBuild to a console application that invokes a DbConnection.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM