更新:
基本上,这归结为"如何在Web API站点的Application Start上强制加载类库,这样我就可以反映一次,并确保我获得某个类的所有实现。或者,如果没有好的方法可以做到这一点,那么允许该库中的类自我注册的最佳方法是什么?
原始问题:
我试图在我的Web API中注册所有在应用程序启动时实现某个接口的类,并将它们放在一个列表中,这样我以后就可以找到它们,而不会在每次调用时通过程序集反映。
看起来相当简单,虽然我以前从未这样做过。所以经过一些谷歌搜索和阅读其他一些Stack Overflow问题后,我创建了一个容器类并提出了一个方法来注册所有具体的实现。简化的解决方案如下所示:
public static void RegisterAllBots()
{
var type = typeof(IRobot);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(type.IsAssignableFrom);
foreach (var t in types)
{
TypeRepo.Add(t.Name.ToLower(), t);
}
}
然后我将RegisterAllBots()放在Global.asax的Application_Start()中。
问题是,如果我开始调试解决方案,有时(但并非总是如此)"冷,"它没有找到实现,只有接口本身。如果我去Build - >在运行之前重建解决方案,它会找到它们。所以我假设这是Visual Studio启动WebHost项目而不重建其他类库项目的问题。
所以我在这里有几个问题。
答案 0 :(得分:6)
AppDomain.CurrentDomain.GetAssemblies()
只会返回已加载到当前app域的程序集,当使用该程序集中的类型时会加载程序集。
每当我需要这样做时,我只是简单地保留了一个集合列表,即
var assemblies = new []
{
typeof(TypeFromAssemblyA).Assembly,
typeof(TypeFromAssemblyB).Assembly
};
这只需要包含每个程序集中的一种类型,以确保程序集加载到当前的应用程序域中。
另一种选择是强制使用Assembly.GetReferencedAssemblies()
和Assembly.Load(AssemblyName)
加载所有引用的程序集。有关此方法的详细信息,请参阅this question。然后,您可以在此之后使用现有代码,因为所有程序集都已加载到应用程序域中。
第三种选择是从目录中的程序集中使用Managed Extensibility Framework (MEF)到Export
以及随后的Import
类型。
您选择哪个选项取决于您的解决方案结构以及您希望如何发现实现。我喜欢第一种方法,因为它明确了哪些程序集将被扫描,但最后一种方法允许更多的可扩展性而无需重新编译。
答案 1 :(得分:1)
这是我在实用程序类中通常使用的方法。
public static List<Type> GetTypes<T>()
{
var results = new List<Type>();
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var assembly in assemblies)
{
var types = assembly.GetTypes()
.Where(t => t.IsAbstract == false
&& (typeof(T).IsInterface == false || t.GetInterfaces().Contains(typeof(T)))
&& (typeof(T).IsClass == false || t.BaseType == typeof(T)))
.ToList();
results.AddRange(types);
}
return results;
}
答案 2 :(得分:0)
首先,您无法从每个类库获得所有类的实现,类实现总是存在于存储在桌面下方的拇指驱动器上的DLL。因此,您必须确定要查找的实现的源。
您的实现具有当前AppDomain
作为源,因此查找已由运行时加载的实现。我想你正在寻找那些没有加载的实现。如果是这样,您必须将自己局限于某些目录集(或从外部配置加载该集描述)以查找包含实现的库(Environment.CurrentDirectory
可能就足够了)。你可以有两个案例
例如,您可以在数据库或配置文件中存储程序集名称和完整类型标识符的列表。如果是这样,您可以尝试通过Reflection API找到这些特定的程序集并加载所需的实现。但是,信息不正确存在问题:您可以使用列表中未提及的实现,或者反过来说,您可以在列表中获得有关实现的信息,这些信息在运行时中不存在。此外,您可以使用辅助dll来引用每个实现,因此当您加载它时 - 您也会将每个实现加载到AppDomain
(从这一点开始,您可以使用初始解决方案)
在这种情况下,只有一种方法。对于源目录中的每个dll:
AppDomain
如果您可以为实施强制执行其他代码支持(它是您自己的代码,或者您对第三方实施者有严格的指导原则),您可以使用Management Extensibility Framework为您处理此任务:您将必须按Export
属性标记每个实现,定义组合目录以查找包含实现的库,并启动组合以将每个实现加载到由ImportMany
属性标记的组合容器。否则,您必须自己准确地实施这些操作,但要注意有问题的情况:
我个人更喜欢在第一种情况下使用第二种方式,因为我不想将对儿童的隐性依赖引入父母。
答案 3 :(得分:0)
如果尚未加载程序集,则可以使用Assembly.LoadAssembly在运行时加载程序集。非常简单的例子:
List<Assembly> loadedAssemblies = new List<Assembly>();
DirectoryInfo di = new DirectoryInfo("path to my assemblies");
foreach (FileInfo fi in di.GetFiles("*.dll"))
{
try
{
loadedAssemblies.Add(Assembly.LoadFrom(fi.FullName));
}
catch (Exception ex)
{
// handle problems loading the assemblies here - there are a boatload of possible failures
}
}
foreach (Assembly a in loadedAssemblies)
{
// Use reflection to do whatever it is that you wanted to do
}
MSDN上提供了其他文档:http://msdn.microsoft.com/en-us/library/1009fa28%28v=vs.110%29.aspx