Инъекция зависимостей в WebAPI с замком Виндзор

Я хочу внедрить Injection Dependency в приложении WebApi с помощью Castle Windsor. У меня есть следующий пример кода -

Интерфейс

public interface IWatch
{
    {
        DateTime GetTime();
    }
}

После класса Watch реализует интерфейс IWatch -

public class Watch:IWatch
{
        public DateTime GetTime()
        {
            return DateTime.Now;
        }
}

Контроллер WebApi - WatchController, как показано ниже -

public class WatchController : ApiController
{
        private readonly IWatch _watch;

        public WatchController()
        {
            _watch = new Watch();
        }

        //http://localhost:48036/api/Watch
        public string Get()
        {
            var message = string.Format("The current time on the server is: {0}", _watch.GetTime());
            return message;
        }
}

В настоящее время я запускаю объект IWatch с помощью Watch в конструкторе WatchController. Я хочу удалить зависимость инициализации IWatch внутри конструктора, используя принцип инъекции зависимостей Windsor Castle.

Может ли кто-нибудь предоставить мне шаги по внедрению инъекции зависимостей в этом случае WebApi? Спасибо заранее!

Ответы

Ответ 1

CodeCaster, Noctis и Cristiano благодарим вас за вашу помощь и руководство. Я только что получил решение для моего запроса -

Первым шагом является использование nuget для установки пакетов Windsor.Castle в WebApi.

enter image description here

Рассмотрим следующий фрагмент кода -

Интерфейс IWatch.cs

public interface IWatch
{
     DateTime GetTime();
}

Класс Watch.cs

public class Watch:IWatch
{
    public DateTime GetTime()
    {
        return DateTime.Now;
    }
}

ApiController WatchController.cs определяется следующим образом: -

public class WatchController : ApiController
{
     private readonly IWatch _watch;

     public WatchController(IWatch watch)
     {
         _watch = watch;
     }

     public string Get()
     {
         var message = string.Format("The current time on the server is: {0}", _watch.GetTime());
         return message;
     }
}

В контроллере мы ввели зависимость через объект IWatch в конструкторе WatchController. Я использовал IDependencyResolver и IDependencyScope для достижения инъекции зависимостей в web api. Интерфейс IDependencyResolver используется для разрешения всего за пределами области запроса.

WindsorDependencyResolver.cs

internal sealed class WindsorDependencyResolver : IDependencyResolver
{
    private readonly IWindsorContainer _container;

    public WindsorDependencyResolver(IWindsorContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }

        _container = container;
    }
    public object GetService(Type t)
    {
        return _container.Kernel.HasComponent(t) ? _container.Resolve(t) : null;
    }

    public IEnumerable<object> GetServices(Type t)
    {
        return _container.ResolveAll(t).Cast<object>().ToArray();
    }

    public IDependencyScope BeginScope()
    {
        return new WindsorDependencyScope(_container);
    }

    public void Dispose()
    {

    }
}

WindsorDependencyScope.cs

internal sealed class WindsorDependencyScope : IDependencyScope
{
    private readonly IWindsorContainer _container;
    private readonly IDisposable _scope;

    public WindsorDependencyScope(IWindsorContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }
        _container = container;
        _scope = container.BeginScope();
    }

    public object GetService(Type t)
    {
        return _container.Kernel.HasComponent(t) ? _container.Resolve(t) : null;
    }

    public IEnumerable<object> GetServices(Type t)
    {
        return _container.ResolveAll(t).Cast<object>().ToArray();
    }

    public void Dispose()
    {
        _scope.Dispose();
    }
}

WatchInstaller.cs

Установщики - это просто типы, которые реализуют интерфейс IWindsorInstaller. Интерфейс имеет один метод, называемый Install. Метод получает экземпляр контейнера, который затем может регистрировать компоненты с использованием свободного API регистрации:

public class WatchInstaller : IWindsorInstaller
{
      public void Install(IWindsorContainer container, IConfigurationStore store)
      {

//Необходимо зарегистрировать контроллеры явно в вашем контейнере // В противном случае получится Исключение:

Произошла ошибка при попытке создать//контроллер типа 'XxxxController. Убедитесь, что контроллер имеет без параметров публичный конструктор.

//Причина: В основном, произошло то, что вы не зарегистрировали свои контроллеры явно в своем контейнере. Windsor пытается разрешить вам незарегистрированные конкретные типы, но поскольку он не может его решить (вызванный ошибкой в ​​вашей конфигурации), он возвращает null. Он вынужден вернуть null, потому что Web API заставляет его делать это из-за контракта IDependencyResolver. Поскольку Windsor возвращает значение null, Web API попытается создать сам контроллер, но поскольку он не имеет конструктора по умолчанию, он выдает исключение "Убедитесь, что контроллер имеет исключение без параметров". Это сообщение об ошибке вводит в заблуждение и не объясняет истинную причину.

***container.Register(Classes.FromThisAssembly()
                            .BasedOn<IHttpController>()
                            .LifestylePerWebRequest());***
          container.Register(
              Component.For<IWatch>().ImplementedBy<Watch>()
          );
      }
}

Наконец, нам нужно заменить преобразователь зависимостей по умолчанию с реализацией Windsor в Global.asax.cs(метод Application_Start) и установить наши зависимости:

    private static IWindsorContainer _container;
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        WebApiConfig.Register(GlobalConfiguration.Configuration);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);

        ConfigureWindsor(GlobalConfiguration.Configuration);
    }

    public static void ConfigureWindsor(HttpConfiguration configuration)
    {
        _container = new WindsorContainer();
        _container.Install(FromAssembly.This());
        _container.Kernel.Resolver.AddSubResolver(new CollectionResolver(_container.Kernel, true));
        var dependencyResolver = new WindsorDependencyResolver(_container);
        configuration.DependencyResolver = dependencyResolver;
    }    

Ответ 2

Я не работал напрямую с Castle Windsor, но я считаю, что логика должна быть аналогичной:

Ваш WatchController ctor должен выглядеть следующим образом:

public WatchController(IWatch watch) 
{
    _watch = watch;
}

И здесь вы inject зависитете.

У вас должен быть эквивалент Locator, в котором вы регистрируете свой класс WatchController, и скажите ему, какие часы он должен получать, в зависимости от того, что вы хотите... дизайн/время выполнения, день недели, случайное число. независимо от того, что работает или что вам нужно...

Следующий код из MVVM-Light, но должен прояснить вышеприведенный параграф:

static ViewModelLocator()
{
    ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

    // This will run in design mode, so all your VS design data will come from here
    if (ViewModelBase.IsInDesignModeStatic)
    {
        SimpleIoc.Default.Register<IDataService, Design.DesignDataService>();
    }
    // This will run REAL stuff, in runtime
    else
    {
        SimpleIoc.Default.Register<IDataService, DataService>();
    }

    // You register your classes, so the framework can do the injection for you
    SimpleIoc.Default.Register<MainViewModel>();
    ...
}