在 ASP.NET 5 中动态加载程序集
Dynamically load assemblies in ASP.NET 5
我曾经有一些代码扫描我的应用程序的 bin
目录以查找尚未加载到 AppDomain 中的程序集并加载它们。它基本上看起来像:
foreach (var assemblyPath in Directory.GetFiles("path\to\bin", "*.dll"))
{
var inspected = Assembly.ReflectionOnlyLoadFrom(assemblyPath);
Assembly.Load(inspected.GetName());
}
为简洁起见,我跳过了 try/catch 子句等。
这允许我在 运行 时将程序集放入 bin 文件夹中,其中包含某些接口的实现,并让 IoC 容器自动拾取它们。现在有了新的 Roslyn 魔法,调试时不再有物理 DLL。有什么方法可以动态检索程序集名称、项目名称或依赖项名称(在 project.json
中)。
我想我必须实现类似 this example in the Entropy repo 的东西,但我不知道如何为我的场景实现它。
您正在寻找的是 ILibraryManager
实现,它提供对应用程序的完整依赖关系图的访问。这已经流经 ASP.NET 5 DI 系统。因此,您可以从那里联系它。
Sample usage can be found inside RoslynCompilationService
.
您可以使用IAssemblyLoadContextAccessor
接口动态加载ASP.NET 5 class 个库(.xproj) 项目。以下示例代码适用于 Beta 4:
public class Startup
{
public void Configure(IApplicationBuilder app)
{
var assemblyLoadContextAccessor = app.ApplicationServices.GetService<IAssemblyLoadContextAccessor>();
var loadContext = assemblyLoadContextAccessor.Default;
var loadedAssembly = loadContext.Load("NameOfYourLibrary");
}
}
我按照@tugberk 的建议使用 ILibraryManager
部分解决了这个问题。我稍微改变了方法,从而不再需要扫描 bin
文件夹以查找新程序集。我只想要当前 AppDomain 中所有已加载的程序集。
我在我的类型查找器 class 中注入了一个 ILibraryManager
接口的实例,并使用了 GetReferencingLibraries()
方法和核心程序集的名称,它被所有其他程序引用应用程序中的程序集。
可以找到示例实现 here,这是重要的部分:
public IEnumerable<Assembly> GetLoadedAssemblies()
{
return _libraryManager.GetReferencingLibraries(_coreAssemblyName.Name)
.SelectMany(info => info.Assemblies)
.Select(info => Assembly.Load(new AssemblyName(info.Name)));
}
对于 .net 核心用户,这是我从特定路径加载程序集的代码。我不得不使用指令,因为 .Net Framework 和 .Net Core 略有不同。
在您的 class header 中,您需要声明使用类似于:
#if NET46
#else
using System.Runtime.Loader;
#endif
并且在您的函数中类似于以下内容:
string assemblyPath = "c:\temp\assmebly.dll";
#if NET46
Assembly assembly = Assembly.LoadFrom(assemblyPath);
#else
AssemblyLoadContext context = AssemblyLoadContext.Default;
Assembly assembly = context.LoadFromAssemblyPath(assemblyPath);
#endif
它不是 ASP.NET,但可以轻松转换为 asp.net。
下面是用于加载程序集的函数,并在该程序集上的 class 中调用方法。
private static FormCustomized loadLayout(global::System.String layoutFilename, global::System.String layoutNameSpace)
{
FormCustomized mainForm = default;
Type typeMainLayout = default;
FileInfo layoutFile;
layoutFile = new FileInfo(layoutFilename);
layoutFile.Refresh();
if (!layoutFile.Exists)
{
MessageBox.Show("Layout file not found. You need to reinstall the program");
return default;
}
try
{
Assembly assemblyRaw = Assembly.LoadFrom(layoutFilename);
AssemblyLoadContext context = AssemblyLoadContext.Default;
Assembly assembly = context.LoadFromAssemblyPath(layoutFilename);
Type typeMainLayoutIni = assembly.GetType(layoutNameSpace + ".initializeLayoutClass");
Object iniClass = Activator.CreateInstance(typeMainLayoutIni, true);
MethodInfo methodInfo = typeMainLayoutIni.GetMethod("AssembliesToLoadAtStart");
enVars.assemblies = (Dictionary<string, Environment.environmentAssembliesClass>)methodInfo.Invoke(iniClass, default);
typeMainLayout = assembly.GetType(layoutNameSpace + ".mainAppLayoutForm");
mainForm = Activator.CreateInstance(typeMainLayout, enVars) as FormCustomized;
}
catch (Exception ex)
{
return default;
}
return default;
}
我曾经有一些代码扫描我的应用程序的 bin
目录以查找尚未加载到 AppDomain 中的程序集并加载它们。它基本上看起来像:
foreach (var assemblyPath in Directory.GetFiles("path\to\bin", "*.dll"))
{
var inspected = Assembly.ReflectionOnlyLoadFrom(assemblyPath);
Assembly.Load(inspected.GetName());
}
为简洁起见,我跳过了 try/catch 子句等。
这允许我在 运行 时将程序集放入 bin 文件夹中,其中包含某些接口的实现,并让 IoC 容器自动拾取它们。现在有了新的 Roslyn 魔法,调试时不再有物理 DLL。有什么方法可以动态检索程序集名称、项目名称或依赖项名称(在 project.json
中)。
我想我必须实现类似 this example in the Entropy repo 的东西,但我不知道如何为我的场景实现它。
您正在寻找的是 ILibraryManager
实现,它提供对应用程序的完整依赖关系图的访问。这已经流经 ASP.NET 5 DI 系统。因此,您可以从那里联系它。
Sample usage can be found inside RoslynCompilationService
.
您可以使用IAssemblyLoadContextAccessor
接口动态加载ASP.NET 5 class 个库(.xproj) 项目。以下示例代码适用于 Beta 4:
public class Startup
{
public void Configure(IApplicationBuilder app)
{
var assemblyLoadContextAccessor = app.ApplicationServices.GetService<IAssemblyLoadContextAccessor>();
var loadContext = assemblyLoadContextAccessor.Default;
var loadedAssembly = loadContext.Load("NameOfYourLibrary");
}
}
我按照@tugberk 的建议使用 ILibraryManager
部分解决了这个问题。我稍微改变了方法,从而不再需要扫描 bin
文件夹以查找新程序集。我只想要当前 AppDomain 中所有已加载的程序集。
我在我的类型查找器 class 中注入了一个 ILibraryManager
接口的实例,并使用了 GetReferencingLibraries()
方法和核心程序集的名称,它被所有其他程序引用应用程序中的程序集。
可以找到示例实现 here,这是重要的部分:
public IEnumerable<Assembly> GetLoadedAssemblies()
{
return _libraryManager.GetReferencingLibraries(_coreAssemblyName.Name)
.SelectMany(info => info.Assemblies)
.Select(info => Assembly.Load(new AssemblyName(info.Name)));
}
对于 .net 核心用户,这是我从特定路径加载程序集的代码。我不得不使用指令,因为 .Net Framework 和 .Net Core 略有不同。
在您的 class header 中,您需要声明使用类似于:
#if NET46
#else
using System.Runtime.Loader;
#endif
并且在您的函数中类似于以下内容:
string assemblyPath = "c:\temp\assmebly.dll";
#if NET46
Assembly assembly = Assembly.LoadFrom(assemblyPath);
#else
AssemblyLoadContext context = AssemblyLoadContext.Default;
Assembly assembly = context.LoadFromAssemblyPath(assemblyPath);
#endif
它不是 ASP.NET,但可以轻松转换为 asp.net。 下面是用于加载程序集的函数,并在该程序集上的 class 中调用方法。
private static FormCustomized loadLayout(global::System.String layoutFilename, global::System.String layoutNameSpace)
{
FormCustomized mainForm = default;
Type typeMainLayout = default;
FileInfo layoutFile;
layoutFile = new FileInfo(layoutFilename);
layoutFile.Refresh();
if (!layoutFile.Exists)
{
MessageBox.Show("Layout file not found. You need to reinstall the program");
return default;
}
try
{
Assembly assemblyRaw = Assembly.LoadFrom(layoutFilename);
AssemblyLoadContext context = AssemblyLoadContext.Default;
Assembly assembly = context.LoadFromAssemblyPath(layoutFilename);
Type typeMainLayoutIni = assembly.GetType(layoutNameSpace + ".initializeLayoutClass");
Object iniClass = Activator.CreateInstance(typeMainLayoutIni, true);
MethodInfo methodInfo = typeMainLayoutIni.GetMethod("AssembliesToLoadAtStart");
enVars.assemblies = (Dictionary<string, Environment.environmentAssembliesClass>)methodInfo.Invoke(iniClass, default);
typeMainLayout = assembly.GetType(layoutNameSpace + ".mainAppLayoutForm");
mainForm = Activator.CreateInstance(typeMainLayout, enVars) as FormCustomized;
}
catch (Exception ex)
{
return default;
}
return default;
}