这是交易:我想创建一个C#控制台应用程序。运行时,此应用程序将在特定文件夹中查找具有实现特定接口的类的dll,然后在这些dll上运行方法。
我之前没有这样做过,但从IoC / Ninject的角度来看,这应该是“足够简单”的。我认为你可以用kernel.Bind()
做某事来加载某个目录中某个接口的程序集。我想/希望我能把这部分弄清楚(如果你不知道的话,请告诉我!)。
但这是我的窘境。
首先是视觉帮助:
MainProgramFolder
-- MainProgram.exe
-- MainProgram.exe.config
-- LibraryFolder
----- Library1Folder
--------Library1.dll
--------Library1.dll.config
----- Library2Folder
--------Library2.dll
--------Library2.dll.config
实现此接口的dll在技术上是独立的应用程序 - 它们只是库而不是exe(或者更确切地说,我希望它们用于IoC目的)。我希望他们可以使用自己的app.configs在自己的上下文中运行。因此,例如,MainProgram.exe会将ILibrary接口绑定到Library1.dll和Library2.dll中的类,因为它们实现了ILibrary。但在 Library1中,它调用ConfigurationManager来获取其设置。当我为MainProgram的每个绑定调用Class.Method()时,如何确保它们引用自己的.config而不是MainProgram.exe.config? (另外,fwiw,这些额外的库可能不是程序集的一部分甚至是主程序的命名空间 - 我们基本上为应用程序提供了一个drop文件夹,以便“订阅”主程序的执行。)
IOW,我知道你可以将app.config附加到类库但是我不知道在从IOC解析绑定之后如何使这些dll“看到”它自己的配置而不是主要的程序的配置。
所有的想法都赞赏!
由于 汤姆
答案 0 :(得分:3)
首先,要加载和绑定所有类,您需要ninject.extensions.conventions,如下所示:
var kernel = new StandardKernel();
/*add relevant loop/function here to make it recurse folders if need be*/
kernel.Bind(s => s.FromAssembliesMatching("Library*.dll")
.Select(type => type.IsClass && type.GetInterfaces().Contains(typeof(ILibrary)))
.BindSingleInterface()
.Configure(x=>x.InSingletonScope()));
要使每个实例加载其配置,就好像它是入口点,您需要在新的应用程序域中运行它。您的ILibrary实现需要继承MarshalByRefObject并且是Serializable,以便它可以在备用appdomain中正确运行
[Serializable]
public class LibraryA :MarshalByRefObject, ILibrary
然后,您可以将此激活策略添加到您的内核中,这将导致它在返回之前将ILibrary实例与使用您的配置文件约定在备用appdomain中加载的实例交换。
public class AlternateAppDomainStrategy<T> : ActivationStrategy
{
public override void Activate(IContext context, InstanceReference reference)
{
if (reference.Instance.GetType().GetInterfaces().Contains(typeof(T)))
{
var type = reference.Instance.GetType();
var configFilePath = type.Assembly.GetName().Name + ".dll.config";
var file = new FileInfo(configFilePath);
if (file.Exists)
{
var setup = new AppDomainSetup() { ConfigurationFile = file.FullName, ApplicationBase = AppDomain.CurrentDomain.BaseDirectory };
var domain = AppDomain.CreateDomain(type.FullName, null, setup);
var instance = domain.CreateInstanceAndUnwrap(type.Assembly.FullName, type.FullName);
reference.Instance = instance;
}
else
{
throw new FileNotFoundException("Missing config file", file.FullName);
}
}
}
}
并将其添加到您的内核
kernel.Components.Add<IActivationStrategy, AlternateAppDomainStrategy<ILibrary>>();
从那里你可以简单地实例化你的ILibrary实例并调用它们的方法。他们将使用自己的配置加载自己的应用程序域。如果你需要通过方法或构造函数将事物传入/传出实例,那么它会变得更复杂,但是如果你没有这样做,那么它应该是正确的。
var libs = kernel.GetAll<ILibrary>();
foreach (var lib in libs)
{
lib.Method();
}