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

C# .net核心:在运行时加载程序集及其非托管依赖项,c#,msbuild,.net-core,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 = Assemb

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

例如,在一个控制台应用程序中,我想从我的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复制到驱动程序包中,它可以工作,但我发现这个想法并不方便


更新

我不知道我需要实例化哪种类型的数据库连接,也不知道我使用的是什么操作系统

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

在.NET世界中,所有程序集(托管或非托管)都位于调试/发布文件夹$(TargetPath)或GAC中。 但是在.NETCore中,我只有deps.json文件

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


更新2

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

生成后,MsBuild任务将加载目标项目app.config文件(对于.NET),该文件应声明一些变量,如:

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

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

最简单的方法是让运行时为您自动处理程序集加载。不要调用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" />
然后,添加一个在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控制主机和程序集的加载。您可能会发现一个更简单的解决方案是启动一个新的.NET核心进程,从MSBuild到调用DbConnection的控制台应用程序

谢谢你,内特!我已经详细说明了我的问题。我做得好吗?你能澄清一些事情吗?如何使用此MSBuild任务?它将如何确定使用哪种类型的数据库连接?连接字符串来自哪里?正确的实现将取决于您是尝试从MSBuild正在编译的应用程序运行代码,还是仅尝试在MSBuild内部作为生成步骤运行某些内容。我更新了答案。你想做的很难。我建议了一种更简单的方法,但完整的解决方案太长,无法在堆栈溢出答案中很好地描述。
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");
    }
}