Skip to content

Instantly share code, notes, and snippets.

@ramonsmits
Last active June 9, 2023 07:29
Show Gist options
  • Select an option

  • Save ramonsmits/aef6872d20192acd403fa883c923032a to your computer and use it in GitHub Desktop.

Select an option

Save ramonsmits/aef6872d20192acd403fa883c923032a to your computer and use it in GitHub Desktop.
Plugin load context that probes if a framework specific version exists

README

System.Data.SqlClient:

Files in build folder:

.\System.Data.SqlClient.dll
.\runtimes\unix\lib\netcoreapp2.1\System.Data.SqlClient.dll
.\runtimes\win\lib\netcoreapp2.1\System.Data.SqlClient.dll

Loads

  • Windows: .\runtimes\win\lib\netcoreapp2.1\System.Data.SqlClient.dll
  • Linux: .\runtimes\unix\lib\netcoreapp2.1\System.Data.SqlClient.dll

Microsoft.Data.SqlClient

Files in build folder:

.\Microsoft.Data.SqlClient.dll
.\runtimes\unix\lib\net6.0\Microsoft.Data.SqlClient.dll
.\runtimes\win\lib\net6.0\Microsoft.Data.SqlClient.dll
.\runtimes\win-arm\native\Microsoft.Data.SqlClient.SNI.dll
.\runtimes\win-arm64\native\Microsoft.Data.SqlClient.SNI.dll
.\runtimes\win-x64\native\Microsoft.Data.SqlClient.SNI.dll
.\runtimes\win-x86\native\Microsoft.Data.SqlClient.SNI.dll

Loads

  • Windows: .\runtimes\win\lib\net6.0\Microsoft.Data.SqlClient.dll
  • Linux: .\runtimes\unix\lib\net6.0\Microsoft.Data.SqlClient.dll
using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Loader;
/// <summary>
/// Based on:
///
/// - https://learn.microsoft.com/en-us/dotnet/core/tutorials/creating-app-with-plugin-support
/// - https://github.com/dotnet/samples/tree/main/core/extensions/AppWithPlugin
/// </summary>
sealed class PluginLoadContext : AssemblyLoadContext
{
readonly string platformPath;
readonly string libPath;
readonly AssemblyDependencyResolver resolver;
// https://learn.microsoft.com/en-us/dotnet/standard/net-standard
static readonly string[] Frameworks = new[]
{
#if NET7_0_OR_GREATER
"net7.0",
#endif
#if NET6_0_OR_GREATER
"net6.0",
#endif
#if NET5_0_OR_GREATER
"net5.0",
#endif
#if NETCOREAPP3_1_OR_GREATER
"netcoreapp3.1",
#endif
#if NETCOREAPP3_0_OR_GREATER
"netcoreapp3.0",
"netstandard2.1",
#endif
#if NETCOREAPP2_1_OR_GREATER
"netcoreapp2.1",
#endif
#if NETCOREAPP2_0_OR_GREATER
"netcoreapp2.0",
"netstandard2.0",
#endif
};
public PluginLoadContext(string pluginPath)
{
var path = Path.GetDirectoryName(pluginPath);
resolver = new AssemblyDependencyResolver(pluginPath);
platformPath = Path.Combine(path, "runtimes", $"win-{RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant()}", "native");
var os = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "win" : "unix";
libPath = Path.Combine(path, "runtimes", os, "lib");
}
protected override Assembly Load(AssemblyName assemblyName)
{
string assemblyPath = ResolveManagedDllPath(assemblyName);
if (assemblyPath != null)
{
return LoadFromAssemblyPath(assemblyPath);
}
return null;
}
protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
{
string libraryPath = ResolveUnmanagedDllPath(unmanagedDllName);
if (libraryPath != null)
{
return LoadUnmanagedDllFromPath(libraryPath);
}
return IntPtr.Zero;
}
string ResolveManagedDllPath(AssemblyName assemblyName)
{
var name = assemblyName.Name;
foreach (var framework in Frameworks)
{
var dllPath = Path.Combine(libPath, framework, $"{name}.dll");
if (File.Exists(dllPath))
{
return dllPath;
}
}
return resolver.ResolveAssemblyToPath(assemblyName);
}
string ResolveUnmanagedDllPath(string unmanagedDllName)
{
string dllPath = resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
if (dllPath != null)
{
return dllPath;
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
dllPath = Path.Combine(platformPath, unmanagedDllName);
if (File.Exists(dllPath))
{
return dllPath;
}
}
return null;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment