Загрузка модулей во все сборки в Ninject

У меня есть пара библиотек классов в моем проекте, и все они используют контейнер Ninject IoC. Я хотел загрузить все модули в StandardKernel за один раз, где найден INinjectModule. Поэтому я использовал:

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

Но по какой-то причине это не работает. Может ли кто-нибудь помочь?

Ответы

Ответ 1

Ну, это часто происходит при объявлении привязок, но загружаются другие модули, где этот модуль пытается разрешить привязку, которая еще не загружена. Это происходит потому, что List<INinjectModule> может не быть в правильном порядке.

Если вы считаете, что это так. Следуйте этой резолюции.

Идея заключается в том, что для каждой сборки будет bootstapper, где загрузчик будет отвечать за загрузку модулей в логическом порядке.

Рассмотрим интерфейс для 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;
    }
}

Ответ 2

Еще одна вещь, которую вы должны проверить, - это то, что класс, который расширяет NinjectModule, является общедоступным, иначе он не будет виден в сборке.

Ответ 3

Я думаю, что не стоит использовать CurrentDomain.GetAllAssemblies(), потому что не все сборки проекта могут быть загружены при запуске программы (некоторые сборки могут быть загружены на действия пользователя, например, или другие события). В этом случае у вас будут исключения с нулевой ссылкой для зависимостей.

Ответ 4

Вы можете использовать отражение для поиска и создания экземпляров модулей Ninject:

BuildManager.GetReferencedAssemblies()
    .Cast<Assembly>()
    .SelectMany(a => a.DefinedTypes)
    .Where(t => typeof(INinjectModule).IsAssignableFrom(t))
    .Select(t => (INinjectModule)Activator.CreateInstance(t))