Ninject + Auto Discovery

时间:2013-11-15 18:09:58

标签: c# ninject inversion-of-control

这是交易:我想创建一个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“看到”它自己的配置而不是主要的程序的配置。

所有的想法都赞赏!

由于 汤姆

1 个答案:

答案 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();
            }