0xc*_*ced 1 .net sqlite entity-framework-core
我想使用SQLite Entity Framework Core Database Provider编写一个应用程序,将其打包为单文件可执行文件(即没有任何 .dll 文件的单个 exe 文件)并使其在 .NET Framework 4.7.2 上运行。
这可能吗?如果可以,如何实现?
是的,这是可能的,但它涉及大量工作,无论是在构建时还是在运行时。
首先,您必须使用Costura Fody 插件将参考文献嵌入为资源。将其添加到您的 csproj 文件中:
<ItemGroup>
<PackageReference Include="Costura.Fody" Version="4.1.0" />
</ItemGroup>
Run Code Online (Sandbox Code Playgroud)
这样做会自动将所有 dll 打包到主可执行文件中并在运行时加载它们。对于托管 dll,一切都是开箱即用的,但本机 dll 需要更多的工作。costura32如果我们将本机库嵌入到名为或 的目录中,Costura 可以负责预加载本机库costura64。通过一些 MSBuild 魔法,我们可以嵌入SQLitePCLRaw.lib.e_sqlite3提供的本机 sqlite dll (这是Microsoft.EntityFrameworkCore.Sqlite包的间接依赖项):
<ItemGroup>
<PackageReference Include="SQLitePCLRaw.lib.e_sqlite3" Version="2.0.4" GeneratePathProperty="true" />
</ItemGroup>
<Target Name="EmbedNativeSQLiteDllWithCostura" BeforeTargets="ResolveAssemblyReferences">
<ItemGroup>
<EmbeddedResource Include="$(PkgSQLitePCLRaw_lib_e_sqlite3)\runtimes\win-x86\native\e_sqlite3.dll">
<Link>costura32\e_sqlite3.dll</Link>
<Visible>false</Visible>
</EmbeddedResource>
<EmbeddedResource Include="$(PkgSQLitePCLRaw_lib_e_sqlite3)\runtimes\win-x64\native\e_sqlite3.dll">
<Link>costura64\e_sqlite3.dll</Link>
<Visible>false</Visible>
</EmbeddedResource>
<Content Remove="@(Content)" Condition="'%(Filename)%(Extension)' == 'e_sqlite3.dll'" />
<ReferenceCopyLocalPaths Remove="@(ReferenceCopyLocalPaths)" Condition="'%(Filename)%(Extension)' == 'e_sqlite3.dll' OR '%(Filename)%(Extension)' == 'SQLitePCLRaw.batteries_v2.dll'" />
</ItemGroup>
</Target>
Run Code Online (Sandbox Code Playgroud)
让我们来分解一下。
首先,我们需要一个显式的PackageReferenceto SQLitePCLRaw.lib.e_sqlite3,GeneratePathProperty="true"以便我们可以访问本机 sqlite dll 路径。
然后我们将 x86 和 x64 本机库嵌入到costura32和中costura64,以便 Costura 在启动时自动加载它们(甚至在Main调用函数之前)。
我们还需要e_sqlite3.dll从Content项目中删除文件,否则所有本机 dll 都会复制到输出目录中。我们在输出目录中不需要它们,因为我们将使用嵌入的。
最后,我们需要从项目中删除e_sqlite3.dll和 ,以便它们不会作为 Costura 资源嵌入。是一个本机库,我们已经嵌入了它。不得嵌入,因为我们将编写自己的 SQLitePCLRaw 初始化程序,并且我们不希望 EF Core 运行默认初始化程序。SQLitePCLRaw.batteries_v2.dllReferenceCopyLocalPathse_sqlite3.dllSQLitePCLRaw.batteries_v2.dllBatteries_V2.Init()
这就是构建部分。现在让我们看看运行时必须做什么。
由于我们阻止SQLitePCLRaw.batteries_v2.dll了 Costura 嵌入,因此默认初始化程序将不会运行(Assembly.Load将返回null)。因此,我们必须在使用SqliteConnection. 我们将重用SQLite3Provider_dynamic_cdecl(来自SQLitePCLRaw.provider.dynamic_cdecl包)并使用我们自己的接口实现来配置它IGetFunctionPointer。我们的实现搜索e_sqlite.dllCostura 自动加载的当前进程的模块,并使用NativeLibrary.TryGetExportSQLitePCLRaw 中的方法:
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using SQLitePCL;
public class ModuleGetFunctionPointer : IGetFunctionPointer
{
private readonly ProcessModule _module;
public static ProcessModule GetModule(string moduleName)
{
var modules = Process.GetCurrentProcess().Modules.Cast<ProcessModule>().Where(e => Path.GetFileNameWithoutExtension(e.ModuleName) == moduleName).ToList();
if (modules.Count == 0)
{
throw new ArgumentException($"Found no modules named '{moduleName}' in the current process.", nameof(moduleName));
}
if (modules.Count > 1)
{
throw new ArgumentException($"Found several modules named '{moduleName}' in the current process.", nameof(moduleName));
}
return modules[0];
}
public ModuleGetFunctionPointer(string moduleName) : this(GetModule(moduleName))
{
}
public ModuleGetFunctionPointer(ProcessModule module)
{
_module = module ?? throw new ArgumentNullException(nameof(module));
}
public IntPtr GetFunctionPointer(string name) => NativeLibrary.TryGetExport(_module.BaseAddress, name, out var address) ? address : IntPtr.Zero;
}
Run Code Online (Sandbox Code Playgroud)
最后,在程序的一开始,我们需要初始化 SQLitePCLRaw 提供程序:
const string name = "e_sqlite3";
SQLite3Provider_dynamic_cdecl.Setup(name, new ModuleGetFunctionPointer(name));
SQLitePCL.raw.SetProvider(new SQLite3Provider_dynamic_cdecl());
Run Code Online (Sandbox Code Playgroud)
一切就绪后,SQLite EF Core 数据库提供程序就可以在 .NET Framework 上的单文件可执行文件中使用。完整的工作示例代码可供参考。
请注意,如果您的目标是 .NET Core 而不是 .NET Framework,则无需这样做(如果需要)。发布单文件可执行文件将开箱即用。
| 归档时间: |
|
| 查看次数: |
692 次 |
| 最近记录: |