Подтвердить что ты не робот

Как я могу начать работу с ASP.NET(5) Core и Castle Windsor для инъекций зависимостей?

История:

Я использовал Castle Windsor с установщиками и установками в соответствии с руководством Castle Windsor с более ранними версиями MVC (pre-6) и WebAPI.

ASP.NET(5) Core включил некоторую поддержку Injection Dependency, но я до сих пор не понял, как ее подключить, и несколько образцов, которые я нашел, выглядят намного иначе, чем то, как я использовал его раньше (с установщиками/установками). Большинство примеров предшествуют выпуску последних версий ASP.NET(5), а некоторые, похоже, имеют устаревшую информацию.

Похоже, что он радикально изменился по сравнению с предыдущей версией корневой установки состава, и даже Microsoft.Framework.DependencyInjection.ServiceProvider может разрешить все зависимости, когда я установил его как резерв замка Windsor DI. Я все еще разбираюсь в деталях, но информации не хватает.

Моя попытка использовать Castle Windsor для DI

Я нашел адаптер вроде этого: контейнер Github Castle.Windsor DI.

Startup.cs

    private static IWindsorContainer container;
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerfactory)
    {
        container = new WindsorContainer();
        app.UseServices(services =>
        {
            // ADDED app.ApplicationServices FOR FALLBACK DI
            container.Populate(services, app.ApplicationServices);
            container.BeginScope();
            return container.Resolve<IServiceProvider>();
        });
        // ... default stuff

WindsorRegistration.cs Я добавил несколько строк, чтобы добавить резервную копию Castle Windsor ILazyComponentLoader.

using Castle.MicroKernel.Lifestyle;
using Castle.MicroKernel.Registration;
using Castle.MicroKernel.Resolvers.SpecializedResolvers;
using Castle.Windsor;
using Microsoft.Framework.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Reflection;

namespace Notes.Infrastructure
{
    /// <summary>
    /// An adapted current autofac code to work with Castle.Windsor container.
    /// https://github.com/aspnet/Home/issues/263
    /// </summary>
    public static class WindsorRegistration
    {
        public static void Populate(
                this IWindsorContainer container,
                IEnumerable<IServiceDescriptor> descriptors,
                IServiceProvider fallbackProvider // ADDED FOR FALLBACK DI
                )
        {
            // ADDED FOR FALLBACK DI
            // http://davidzych.com/2014/08/27/building-the-castle-windsor-dependency-injection-populator-for-asp-net-vnext/
            // Trying to add a fallback if Castle Windsor doesn't find the .NET stuff
            var fallbackComponentLoader = new FallbackLazyComponentLoader(fallbackProvider);
            container.Register(Component.For<ILazyComponentLoader>().Instance(fallbackComponentLoader));

            // Rest as usual from the Github link
            container.Register(Component.For<IWindsorContainer>().Instance(container));
            container.Register(Component.For<IServiceProvider>().ImplementedBy<WindsorServiceProvider>());
            container.Register(Component.For<IServiceScopeFactory>().ImplementedBy<WindsorServiceScopeFactory>());

            container.Kernel.Resolver.AddSubResolver(new CollectionResolver(container.Kernel));

            Register(container, descriptors);
        }

        private static void Register(
                IWindsorContainer container,
                IEnumerable<IServiceDescriptor> descriptors)
        {
            foreach (var descriptor in descriptors)
            {
                if (descriptor.ImplementationType != null)
                {
                    // Test if the an open generic type is being registered
                    var serviceTypeInfo = descriptor.ServiceType.GetTypeInfo();
                    if (serviceTypeInfo.IsGenericTypeDefinition)
                    {
                        container.Register(Component.For(descriptor.ServiceType)
                                                .ImplementedBy(descriptor.ImplementationType)
                                                .ConfigureLifecycle(descriptor.Lifecycle)
                                                .OnlyNewServices());
                    }
                    else
                    {
                        container.Register(Component.For(descriptor.ServiceType)
                                                .ImplementedBy(descriptor.ImplementationType)
                                                .ConfigureLifecycle(descriptor.Lifecycle)
                                                .OnlyNewServices());
                    }
                }
                else if (descriptor.ImplementationFactory != null)
                {
                    var service1 = descriptor;
                    container.Register(Component.For(descriptor.ServiceType)
                            .UsingFactoryMethod<object>(c =>
                            {
                                var builderProvider = container.Resolve<IServiceProvider>();
                                return
                                    service1.ImplementationFactory(builderProvider);
                            })
                            .ConfigureLifecycle(descriptor.Lifecycle)
                            .OnlyNewServices());
                }
                else
                {
                    container.Register(Component.For(descriptor.ServiceType)
                            .Instance(descriptor.ImplementationInstance)
                            .ConfigureLifecycle(descriptor.Lifecycle)
                            .OnlyNewServices());
                }
            }
        }

        private static ComponentRegistration<object> ConfigureLifecycle(
                this ComponentRegistration<object> registrationBuilder,
                LifecycleKind lifecycleKind)
        {
            switch (lifecycleKind)
            {
                case LifecycleKind.Singleton:
                    registrationBuilder.LifestyleSingleton();
                    break;

                case LifecycleKind.Scoped:
                    registrationBuilder.LifestyleScoped();
                    break;

                case LifecycleKind.Transient:
                    registrationBuilder.LifestyleTransient();
                    break;
            }

            return registrationBuilder;
        }

        private class WindsorServiceProvider : IServiceProvider
        {
            private readonly IWindsorContainer _container;

            public WindsorServiceProvider(IWindsorContainer container)
            {
                _container = container;
            }

            public object GetService(Type serviceType)
            {
                return _container.Resolve(serviceType);
            }
        }

        private class WindsorServiceScopeFactory : IServiceScopeFactory
        {
            private readonly IWindsorContainer _container;

            public WindsorServiceScopeFactory(IWindsorContainer container)
            {
                _container = container;
            }

            public IServiceScope CreateScope()
            {
                return new WindsorServiceScope(_container);
            }
        }

        private class WindsorServiceScope : IServiceScope
        {
            private readonly IServiceProvider _serviceProvider;
            private readonly IDisposable _scope;

            public WindsorServiceScope(IWindsorContainer container)
            {
                _scope = container.BeginScope();
                _serviceProvider = container.Resolve<IServiceProvider>();
            }

            public IServiceProvider ServiceProvider
            {
                get { return _serviceProvider; }
            }

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

Первая попытка икоты и разрешения

В этом примере я получал:

Исключение типа "Castle.MicroKernel.ComponentNotFoundException" произошло в Castle.Windsor.dll, но не было обработано в коде пользователя Дополнительная информация: компонент для поддержки сервиса Microsoft.Framework.Runtime.IAssemblyLoaderEngine не найден.

Он не был доступен, глядя в отладчик в Castle Fallback - Microsoft.Framework.DependencyInjection.ServiceProvider (таблица услуг).

Из http://davidzych.com/tag/castle-windsor/ Я попытался добавить Fallback, так как Windsor не смог разрешить все зависимости ASP.NET.

FallbackLazyComponentLoader.cs

/// <summary>
/// https://github.com/davezych/DependencyInjection/blob/windsor/src/Microsoft.Framework.DependencyInjection.Windsor/FallbackLazyComponentLoader.cs
/// </summary>
public class FallbackLazyComponentLoader : ILazyComponentLoader
{
    private IServiceProvider _fallbackProvider;

    public FallbackLazyComponentLoader(IServiceProvider provider)
    {
        _fallbackProvider = provider;
    }

    public IRegistration Load(string name, Type service, IDictionary arguments)
    {
        var serviceFromFallback = _fallbackProvider.GetService(service);
        if (serviceFromFallback != null)
        {
            return Component.For(service).Instance(serviceFromFallback);
        }
        return null;
    }
}

Казалось бы, это необходимо (для ввода всех зависимостей .NET)

Я мог бы прокомментировать startup.cs app.UseBrowserLink(); чтобы избавиться от исключения IAssemblyLoaderEngine.

        if (string.Equals(env.EnvironmentName, "Development", StringComparison.OrdinalIgnoreCase))
        {
            //app.UseBrowserLink(); // 

Теперь я столкнулся с исключением:

Исключение типа "System.Reflection.TargetInvocationException" произошло в mscorlib.dll, но не было обработано в коде пользователя

Попытка получить услугу: {Name = "IUrlHelper" FullName = "Microsoft.AspNet.Mvc.IUrlHelper" }

    public IRegistration Load(string name, Type service, IDictionary arguments)
    {
        var serviceFromFallback = _fallbackProvider.GetService(service);

Как двигаться вперед?

Что не так с этой попыткой подключить Castle Windsor DI к ASP.NET(5) Core?

4b9b3361

Ответ 1

Пока я не думаю, что вы можете использовать Castle Windsor Container в качестве контейнера DI, потому что Windsor не поддерживает новый DNVM. Но AutoFac делает, и они следуют тому же правилу.

В Startup.cs существует метод ConfigureServices, возвращающий тип которого недействителен. Вы можете изменить тип возврата на ISerivceProvider и вернуть конкретный IServiceProvider, система будет использовать новый IServiceProvider как контейнер DI по умолчанию. Ниже приведен пример AutoFac.

public IServiceProvider ConfigureServices(IServiceCollection services)
{
       services.Configure<AppSettings>(Configuration.GetSubKey("AppSettings"));
       services.AddMvc();

       var builder = new ContainerBuilder();
       AutofacRegistration.Populate(builder, services);
       var container = builder.Build();
       return container.Resolve<IServiceProvider>();
}

Другие адаптеры DI также реализовали аналогичные интерфейсы. Вы можете попробовать себя, но обратите внимание, что AutoFac теперь находится в бета-версии 5, поэтому вам нужно сделать некоторую настройку, чтобы сделать ваше приложение запущенным.

Надеюсь, что это поможет

Ответ 2

В вашем вопросе многое происходит, и, честно говоря, я не понимаю все это.

Однако в каталоге MvcSiteMapProvider есть рабочий корневой состав Castle Windsor, который приветствуется реверсоном. Выполните следующие шаги, чтобы получить демо-проект для рабочего проекта для Windsor:

  • Создайте новый проект MVC 5.
  • Установите MvcSiteMapProvider.MVC5.DI.Windsor.
  • Проанализируйте следующие файлы для базовой структуры:
    • /App_Start/DIConfig.cs
    • /App_Start/CompositionRoot.cs
    • /DI/InjectableControllerFactory.cs
    • /DI/Windsor/WindsorDependencyInjectionContainer.cs
    • /DI/Windsor/Installers/MvcInstaller.cs
    • /DI/Windsor/Installers/MvcSiteMapProviderInstaller.cs

Как только у вас будет эта рабочая конфигурация, вы можете ее реорганизовать и добавить к ней в соответствии с вашими требованиями.

Как я помню, не было никаких изменений, необходимых для настройки конфигурации MVC 4 DI с MVC 5. Таким образом, проблема, с которой вы работаете, скорее всего одно из следующего:

  • Вы используете сторонний компонент DI, который несовместим с MVC 5.
  • Вы используете DependencyResolver, и ваша конфигурация не содержит необходимый код для разрешения зависимостей MVC 5.
  • Вы используете расширенные функции Castle Windsor, которые мы не используем, и неправильно их сконфигурировали.

ControllerFactory vs DependencyResolver

Обратите внимание, что в соответствии с Инъекция зависимостей в .NET от Mark Seemann (что я настоятельно рекомендую), не рекомендуется использовать IDependencyResolver с Castle Windsor, потому что гарантирует утечку ресурсов. На самом деле, это, вероятно, самый убедительный аргумент, который он делает для того, чтобы объявить локатор службы как анти-шаблон.

Рекомендуемый подход - использовать IControllerFactory как точку интеграции в MVC, которая реализует метод ReleaseController для решения этой проблемы.

Ответ 3

Таким образом, глядя на ваш код, буквально все это можно заменить библиотекой Castle.Windsor.MsDependencyInjection.

Добавьте Castle.Windsor.MsDependencyInjection в свой проект, затем используйте так:

    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();

        // Normal component registration can go here...

        return WindsorRegistrationHelper.CreateServiceProvider(yourWindsorContainer, services);
    }