我的项目是这样设置的:
- 项目“定义”
- 项目实施”
- 项目“消费者”
项目“消费者”同时引用了“定义”和“实施”,但是没有静态引用“实施”中的任何类型。
当应用程序启动时,“消费者”项目将在“定义”中调用一个静态方法,该方法需要在“实施”中查找类型
有没有一种方法可以强制将任何引用的程序集加载到App Domain中,而无需知道路径或名称,并且最好不必使用成熟的IOC框架?
我的项目是这样设置的:
项目“消费者”同时引用了“定义”和“实施”,但是没有静态引用“实施”中的任何类型。
当应用程序启动时,“消费者”项目将在“定义”中调用一个静态方法,该方法需要在“实施”中查找类型
有没有一种方法可以强制将任何引用的程序集加载到App Domain中,而无需知道路径或名称,并且最好不必使用成熟的IOC框架?
Answers:
这似乎可以解决问题:
var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();
var loadedPaths = loadedAssemblies.Select(a => a.Location).ToArray();
var referencedPaths = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll");
var toLoad = referencedPaths.Where(r => !loadedPaths.Contains(r, StringComparer.InvariantCultureIgnoreCase)).ToList();
toLoad.ForEach(path => loadedAssemblies.Add(AppDomain.CurrentDomain.Load(AssemblyName.GetAssemblyName(path))));
正如Jon指出的那样,理想的解决方案将需要递归到每个已加载程序集的依赖关系,但是在我的特定情况下,我不必担心它。
更新: .NET 4中包含的托管扩展框架(System.ComponentModel)具有更好的工具来完成这样的事情。
new DirectoryCatalog(".");
需要参考System.ComponentModel.Composition
)。
assembly.Location
。那一个也需要检查。不适用于IsDynamic
程序集。
您可以使用Assembly.GetReferencedAssemblies
获取AssemblyName[]
,然后分别调用Assembly.Load(AssemblyName)
它们。当然,您需要递归-但最好跟踪已加载的程序集:)
System.Reflection
)发现可用服务时,它自然不会找到包含在尚未加载的程序集中的服务。从那时起,我的默认方法就是从应用程序的CompositionRoot中每个引用程序集的随机类型创建一个虚拟子类,以确保所有依赖项均已就绪。我希望我可以通过预先加载所有内容来跳过这些废话,即使是以进一步增加启动时间为代价。@JonSkeet还有另一种方法吗?thx
只是想分享一个递归示例。我在启动例程中这样调用LoadReferencedAssembly方法:
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
this.LoadReferencedAssembly(assembly);
}
这是递归方法:
private void LoadReferencedAssembly(Assembly assembly)
{
foreach (AssemblyName name in assembly.GetReferencedAssemblies())
{
if (!AppDomain.CurrentDomain.GetAssemblies().Any(a => a.FullName == name.FullName))
{
this.LoadReferencedAssembly(Assembly.Load(name));
}
}
}
name
是不是已经AppDomain.CurrentDomain.GetAssemblies()
,这意味着如果它只会再次出现foreach
回升AssemblyName
还没有被加载。
O(n^2)
对此算法的运行时间不满意(GetAssemblies().Any(...)
在foreach
))。我会用aHashSet
将其归纳为O(n)
。
如果您使用Fody.Costura或任何其他程序集合并解决方案,则接受的答案将不起作用。
以下将加载任何当前加载的装配的参考装配。递归留给您。
var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();
loadedAssemblies
.SelectMany(x => x.GetReferencedAssemblies())
.Distinct()
.Where(y => loadedAssemblies.Any((a) => a.FullName == y.FullName) == false)
.ToList()
.ForEach(x => loadedAssemblies.Add(AppDomain.CurrentDomain.Load(x)));
!y.IsDynamic
一下.Where
鉴于今天我必须从特定路径加载程序集和依赖项,因此我编写了此类来实现。
public static class AssemblyLoader
{
private static readonly ConcurrentDictionary<string, bool> AssemblyDirectories = new ConcurrentDictionary<string, bool>();
static AssemblyLoader()
{
AssemblyDirectories[GetExecutingAssemblyDirectory()] = true;
AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly;
}
public static Assembly LoadWithDependencies(string assemblyPath)
{
AssemblyDirectories[Path.GetDirectoryName(assemblyPath)] = true;
return Assembly.LoadFile(assemblyPath);
}
private static Assembly ResolveAssembly(object sender, ResolveEventArgs args)
{
string dependentAssemblyName = args.Name.Split(',')[0] + ".dll";
List<string> directoriesToScan = AssemblyDirectories.Keys.ToList();
foreach (string directoryToScan in directoriesToScan)
{
string dependentAssemblyPath = Path.Combine(directoryToScan, dependentAssemblyName);
if (File.Exists(dependentAssemblyPath))
return LoadWithDependencies(dependentAssemblyPath);
}
return null;
}
private static string GetExecutingAssemblyDirectory()
{
string codeBase = Assembly.GetExecutingAssembly().CodeBase;
var uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
return Path.GetDirectoryName(path);
}
}
还有一种版本(基于Daniel Schaffer的回答)是您可能不需要加载所有程序集,而是需要预定义的数量时的情况:
var assembliesToLoad = { "MY_SLN.PROJECT_1", "MY_SLN.PROJECT_2" };
// First trying to get all in above list, however this might not
// load all of them, because CLR will exclude the ones
// which are not used in the code
List<Assembly> dataAssembliesNames =
AppDomain.CurrentDomain.GetAssemblies()
.Where(assembly => AssembliesToLoad.Any(a => assembly.GetName().Name == a))
.ToList();
var loadedPaths = dataAssembliesNames.Select(a => a.Location).ToArray();
var compareConfig = StringComparison.InvariantCultureIgnoreCase;
var referencedPaths = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll")
.Where(f =>
{
// filtering the ones which are in above list
var lastIndexOf = f.LastIndexOf("\\", compareConfig);
var dllIndex = f.LastIndexOf(".dll", compareConfig);
if (-1 == lastIndexOf || -1 == dllIndex)
{
return false;
}
return AssembliesToLoad.Any(aName => aName ==
f.Substring(lastIndexOf + 1, dllIndex - lastIndexOf - 1));
});
var toLoad = referencedPaths.Where(r => !loadedPaths.Contains(r, StringComparer.InvariantCultureIgnoreCase)).ToList();
toLoad.ForEach(path => dataAssembliesNames.Add(AppDomain.CurrentDomain.Load(AssemblyName.GetAssemblyName(path))));
if (dataAssembliesNames.Count() != AssembliesToLoad.Length)
{
throw new Exception("Not all assemblies were loaded into the project!");
}
在我的winforms应用程序中,我使JavaScript(在WebView2控件中)可以调用各种.NET事物,例如Microsoft.VisualBasic.Interaction
,程序集Microsoft.VisualBasic.dll中的方法(例如InputBox()
etc)。
但是我的应用程序本身并未使用该程序集,因此该程序集永远不会加载。
因此,为了强制加载程序集,我最终将其简单地添加到了Form1_Load中:
if (DateTime.Now < new DateTime(1000, 1, 1, 0, 0, 0)) { // never happens
Microsoft.VisualBasic.Interaction.Beep();
// you can add more things here
}
编译器认为可能需要该程序集,但实际上这是绝对不会发生的。
这不是一个非常复杂的解决方案,但是又快速又肮脏。