在Ninject中加载所有程序集中的模块

16

我在项目中有几个类库,所有的类库都使用Ninject IoC容器。我想要在一个StandardKernel中一次性加载所有的模块,无论何时发现一个INinjectModule。因此,我使用了以下代码:

var kernel = new StandardKernel();
kernel.Load(AppDomain.CurrentDomain.GetAssemblies())

但是出于某种原因,这并不起作用。有人可以帮忙吗?


不确定你能否在一行代码中完成这个操作。https://dev59.com/vkrSa4cB1Zd3GeqPUTE3 - Jason Evans
请检查AppDomain.CurrentDomain.GetAssemblies()返回的内容,也许你正在寻找的程序集尚未被加载? - tpeczek
@JasonEvans 我试过了,但它也没有帮助 :( - user568164
你如何加载其他程序集? - Remo Gloor
在我的情况下,由于项目松散耦合,包含Ninject模块的dll未被主项目引用,因此该dll不在主项目的bin文件夹中。在我更改了输出路径后,kernel.Load("projectName.dll"); 就可以正常工作了。 - Reza Abolfathi
显示剩余4条评论
4个回答

22

当声明绑定但加载其他模块时,经常会出现这种情况,其中该模块尝试解析尚未加载的绑定。这是因为List<INinjectModule>可能不在正确的顺序中。

如果您认为这是原因,请按照以下解决方案进行操作。

我们的想法是为每个程序集提供一个bootstrapper,其中bootstrapper将负责按其逻辑顺序加载模块。

让我们考虑一个用于引导启动器的接口(我们将使用此接口在程序集中查找引导启动器)

public interface INinjectModuleBootstrapper
{
    IList<INinjectModule> GetModules();
}

现在考虑为您的DataAccess程序集实现INinjectModuleBootstrapper
public class DataAccessBootstrapper : INinjectModuleBootstrapper
{
    public IList<INinjectModule> GetModules()
    {
        //this is where you will be considering priority of your modules.
        return new List<INinjectModule>()
                   {
                       new DataObjectModule(),
                       new RepositoryModule(),
                       new DbConnectionModule()
                   };
        //RepositoryModule cannot be loaded until DataObjectModule is loaded
        //as it is depended on DataObjectModule and DbConnectionModule has
        //dependency on RepositoryModule
    }
}

这是如何为您的所有程序集定义Bootstrapper的方法。现在,在程序启动时,我们需要StandardKernel,其中加载了所有模块。我们将编写类似以下内容的代码:

var assemblies = AppDomain.CurrentDomain.GetAssemblies();
return BootstrapHelper.LoadNinjectKernel(assemblies);

我们的 BootstrapperHelper 类如下:

public static class BootstrapHelper
{
    public static StandardKernel LoadNinjectKernel(IEnumerable<Assembly> assemblies)
    {
        var standardKernel = new StandardKernel();
        foreach (var assembly in assemblies)
        {
            assembly
                .GetTypes()
                .Where(t =>
                       t.GetInterfaces()
                           .Any(i =>
                                i.Name == typeof(INinjectModuleBootstrapper).Name))
                .ToList()
                .ForEach(t =>
                             {
                                 var ninjectModuleBootstrapper =
                                     (INinjectModuleBootstrapper)Activator.CreateInstance(t);

                                 standardKernel.Load(ninjectModuleBootstrapper.GetModules());
                             });
        }
        return standardKernel;
    }
}

1
很棒的答案!然而,在搜索了一段时间后,我发现在Global.asax的Application_Start中为什么没有调用我的所有程序集,我找到了这个有趣的线程:https://dev59.com/J3bZa4cB1Zd3GeqPHIwZ。不要使用AppDomain.CurrentDomain.GetAssemblies(),而是调用var assemblies = BuildManager.GetReferencedAssemblies(); - Samuel

4

另外一个需要检查的是继承 NinjectModule 的类是否为 public,否则它将无法在程序集中被找到。


0
您可以使用反射来查找和实例化Ninject模块:
BuildManager.GetReferencedAssemblies()
    .Cast<Assembly>()
    .SelectMany(a => a.DefinedTypes)
    .Where(t => typeof(INinjectModule).IsAssignableFrom(t))
    .Select(t => (INinjectModule)Activator.CreateInstance(t))

0

我认为使用CurrentDomain.GetAllAssemblies() 不是一个好主意,因为并非所有项目程序集都可以在程序启动时加载(例如,某些程序集可能会在用户操作或其他事件上加载)。在这种情况下,您将遇到依赖项的空引用异常。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接