.net核心:在运行时加载程序集及其非托管依赖项

时间:2017-02-10 23:05:14

标签: c# msbuild .net-core

我希望能够在运行时加载数据库驱动程序程序集并执行与服务器的连接。

例如,在控制台应用程序中,我想从我的nuget包中加载 SQLite驱动程序并加载类型: SqliteConnection 。这是我的伪代码:

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();

直到 cnn.Open()点,它无法找到非托管依赖项:

  

System.DllNotFoundException:'无法加载DLL的sqlite3'

我对Sql Server和本机依赖项有相同的行为:

  

System.DllNotFoundException:'无法加载DLL'sni.dll'

如果我在驱动程序包中复制dll它可以工作,但我觉得这个想法不方便。

更新

我不知道我需要实例化哪种DbConnection,也不知道我正在使用的os。

上下文是我的库实现Microsoft.Build.Utilities.Task并在项目构建后由 MsBuild.exe 启动。 所以我想要DbConnection的应用程序的 $(TargetPath)(以及配置文件或其他地方的驱动程序程序集名称)。

在.NET世界中,所有程序集(托管与否)都在调试/发布文件夹 $(TargetPath)或GAC中。 但在.net核心中我只有deps.json文件。

所以我的猜测:使用DependencyContext类来读取deps.json文件。 找到它使用的数据库驱动程序,并将程序集及其所有依赖项(取决于操作系统)复制到临时文件夹中,最后我可以实例化我想要的DbConnection类。

更新2

我的目标是在构建之后能够运行使用我开发的nuget包的任何应用程序的sql迁移脚本。 所以我的代码在MSBuild任务中运行,而不是在目标应用程序中运行。

构建之后,MsBuild Task加载目标项目app.config文件(对于.NET),该文件应该声明一些变量,如:

  • 项目中使用的数据库驱动程序
  • 连接字符串
  • 迁移脚本文件夹...

使用这些数据,然后我创建一个DbConnection并运行在指定文件夹中找到的脚本。

1 个答案:

答案 0 :(得分:0)

最简单的方法是让运行时自动处理程序集加载。别打电话给Assembly.Load。而是将Microsoft.Data.Sqlite和System.Data.SqlClient NuGet包的引用添加到项目中。使用csproj和VS 2017,这看起来像:

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

如果您使用的是project.json,则为

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

然后,添加一个在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");
    }
}

尝试自己实施装配加载逻辑会让你陷入一个你不想要的伤害世界。坚持使用NuGet包。

这是有问题的,当然,如果你事先不知道你想要哪种DbConnection。如果这是您的方案,那么您需要将所有非托管和托管库从NuGet缓存复制到应用程序基目录(具有Program.Main的程序集的位置)。如果您希望.NET Core在多个操作系统(Linux,macOS)和平台(x86,x64,ARM)上运行,这不是一种推荐的方法,并且不会起作用。

修改

由于MSBuild控制主机和程序集加载,因此在MSBuild中执行非托管代码可能会非常困难。您可能会发现一个更简单的解决方案是从MSBuild启动一个新的.NET Core进程到一个调用DbConnection的控制台应用程序。