0

Я только начал использовать замок Виндзор (3.3.0) в первый раз, и я застрял на регистрации на основе конвенций.Замок Виндзор, переориентированный зарегистрированный компонент

Я хотел бы зарегистрировать как можно больше по имени конвенция (IDummyService ->DummyService):

var container = new WindsorContainer(); 
container.Register(Types.FromThisAssembly().Pick().WithServiceDefaultInterfaces()); 
container.Register(Component.For<IDummyService>().ImplementedBy<DummyService>().LifestyleSingleton()); // So long, I'm throwing here... 

... но, конечно, чтобы иметь возможность изменить некоторые только зарегистрированные компоненты немного (изменение управление жизненным циклом, параметры конструктора и т. д.)

Поскольку я хочу, чтобы регистрация была такой простой, как это могло бы быть, я хотел бы избежать сложных условий. Только решение, которое я нашел простое - переместить пользовательские вещи выше название конвенции:

var container = new WindsorContainer(); 
container.Register(Component.For<IDummyService>().ImplementedBy<DummyService>().LifestyleSingleton()); // Do custom stuff first... 
container.Register(Types.FromThisAssembly().Pick().WithServiceDefaultInterfaces()); // And convention at the end... 

Мой вопрос сейчас, это правильный путь, как решить мою регистрацию? Я вижу, что Castle Windsor довольно могущественен в этой области и скорее решит мою задачу должным образом, к сожалению, не вижу много реальных примеров.

ответ

1

Одним из вариантов было бы сделать пользовательскую конфигурацию путем реализации интерфейса IContributeComponentModelConstruction - метод ProcessModel вызывается для каждого компонента:

public class ExtraConfiguration : IContributeComponentModelConstruction 
{ 
    public void ProcessModel(IKernel kernel, ComponentModel model) 
    { 
     if (model.Implementation == typeof(DummyService)) 
     { 
      model.LifestyleType = LifestyleType.Singleton; 
     } 

     if ... 
    } 
} 

Вы бы тогда необходимо зарегистрировать это с контейнером до регистрации другого компоненты:

container.Kernel.ComponentModelBuilder.AddContributor(new ExtraConfiguration()); 
+0

Спасибо за подсказку, это может решить одну из моих проблем, но я боюсь, что не хочу писать такую ​​вещь для каждого специального компонента, который у меня может быть - я бы в конечном итоге оказался в разной степени, как ручная регистрация всего. Но спасибо за указание этой опции! –

1

Ваша последняя регистрация линии является чрезвычайно широк и в то время как он будет работать в простом приложении, в большинстве реальных приложений, вероятно, слишком упрощенные.

Обычно сборка предоставляет один или несколько наборов услуг. Используя различные методы выбора (такие как InNamespace, BasedOn, Where), вы можете зарегистрировать каждый набор сервисов и настроить их жизненные циклы, зависимости, имена и т. Д. Я, как правило, создаю отдельный метод для каждого набора сервисов. (например, RegisterDataAccessComponents()). Очень подробно о наборах сервисов, предоставляемых сборкой, значительно облегчает пересмотр кода позже и выяснение того, что предоставляется, и отслеживание конфигурации, которая влияет на поведение во время выполнения. Вы по-прежнему регистрируетесь по соглашению, но вы делаете это немного более четко.

С этой целью я нахожу, что создание реализаций IWindsorInstaller, которые берут на себя ответственность за регистрацию и подключение наборов сервисов, предлагаемых сборкой, также помогает отделить инициализацию контейнера от других задач инициализации приложения.

+0

Привет, да, я знаю об этом. Не могли бы вы указать мне на любой исходный код, где используется Windsor, и приложение не так просто? Что меня раздражает, так это то, что я все равно кончаю кучу регистраций/фильтров (иногда пространство имен содержит классы моделей, которые я не хочу регистрировать) - в этом случае есть тонкая линия (для меня) между тем, что N регистраций с некоторыми фильтрации и M ручных регистраций компонентов, которые мне нужны (где M только бит выше N). –

1

Замок Виндзор действительно очень мощная и зрелая платформа для инъекции зависимости. Он имеет множество внутренних расширений, которые фактически позволяют делать то, к чему вы стремитесь. См. here регистрация служб сборки, встроенных в инструмент.

Я использую его в течение почти 5 лет, и для меня следующие работы лучший способ:

 // at the startup of the application 
     _container = (new WindsorContainer() 
      .AddHelperFacilities() // IWindsorContainer extension that comes from Framework.InversionOfControl 
      .AddWebApiAdapter() // IWindsorContainer extension that comes from Framework.InversionOfControl.WebApi 
      .InitializeDomainUsingConventions( // IWindsorContainer extension that comes from Framework.InversionOfControl 
       AppDomain.CurrentDomain, // domain for which container will be building registrations 
       "ApplicationName.*", // regext to speed up registration process by processing only services from application namespace 
       new WebApiControllersRegistrationConvention(), new DefaultRegistrationConvention())); // one or more conventions 
    // DefaultRegistrationConvention() comes from Framework.InversionOfControl 
    // WebApiControllersRegistrationConvention() comes from Framework.InversionOfControl.WebApi . A separate assembly to be referenced to avoid extra dependancies on Asp.NET WebApi assemblies 
      .Resolve<IApplication>.Start(); // resolves application specific entry point and launches the application 

а затем Framework.InversionOfControl:

namespace Framework.InversionOfControl 
{ 
    public static class WindowsContainerExtensions 
    { 
     public static IWindsorContainer InitializeDomainUsingConventions(
      this IWindsorContainer container, AppDomain appDomain, string commonNamespaceDenominatorMask, params IRegistrationConvention[] registrationConventions) 
     { 
      var assembliesToInitialize = new List<Assembly>(); 
      var runtimeAssemblies = new List<Assembly> { Assembly.GetCallingAssembly() }; 
      var processedAssemblies = new List<Assembly>(); 
      runtimeAssemblies.AddRange(appDomain.GetAssemblies()); 
      foreach (var assembly in runtimeAssemblies) 
      { 
       ProcessAssembly(assembly, assembliesToInitialize, processedAssemblies, commonNamespaceDenominatorMask, commonNamespaceDenominatorMask == null); 
      } 
      var allRuntimeTypes = new List<Type>(); 
      foreach (var assembly in assembliesToInitialize) 
      { 
       var assemblyTypes = assembly.GetTypes().ToList(); 
       var installerTypes = assemblyTypes.Where(t => !t.IsInterface && !t.IsAbstract && t.GetInterfaces().Contains(typeof(IWindsorInstaller))).ToArray(); 
       if (installerTypes.Any()) 
       { 
        foreach (var installer in installerTypes.Select(installerType => (IWindsorInstaller)Activator.CreateInstance(installerType))) 
        { 
         container.Install(installer); 
        } 
       } 
       else 
       { 
        allRuntimeTypes.AddRange(assemblyTypes); 
       } 
      } 
      foreach (var registrationConvention in registrationConventions) 
      { 
       registrationConvention.RegisterTypesUsingConvention(container, allRuntimeTypes); 
      } 
      return container; 
     } 

     private static void ProcessAssembly(Assembly assembly, List<Assembly> assemblies, List<Assembly> processedAssemblies, string commonNamespaceDenominatorMask, bool fullScan) 
     { 
      if (processedAssemblies.Any(x => x.FullName == assembly.FullName)) return; 
      if (assembly == typeof(WindowsContainerExtensions).Assembly) return; 
      processedAssemblies.Add(assembly); 
      var initialize = (new Regex(commonNamespaceDenominatorMask, RegexOptions.IgnoreCase)).Match(assembly.FullName).Success; 
      if (initialize && assemblies.Any(x => x.FullName == assembly.FullName)) 
      { 
       initialize = false; 
      } 
      if (initialize) 
      { 
       assemblies.Add(assembly); 
      } 

      foreach (var referencedAssembliyNames in assembly.GetReferencedAssemblies()) 
      { 
       var referencedAssembliyNames1 = referencedAssembliyNames; 
       if (assemblies.Any(x => x.FullName == referencedAssembliyNames1.FullName)) continue; 
       if (fullScan == false && (new Regex(commonNamespaceDenominatorMask, RegexOptions.IgnoreCase)).Match(assembly.FullName).Success == false) continue; 
       Assembly referencedAssembliy; 
       try 
       { 
        referencedAssembliy = Assembly.Load(referencedAssembliyNames); 
       } 
       catch 
       { 
        continue; 
       } 
       ProcessAssembly(referencedAssembliy, assemblies, processedAssemblies, commonNamespaceDenominatorMask, fullScan); 
      } 
     } 

     public static IWindsorContainer AddHelperFacilities(this IWindsorContainer container) 
     { 
      container.AddFacility<TypedFactoryFacility>(); 

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

      container.Register(Component.For<IWindsorContainer>().ImplementedBy<WindsorContainer>()); 
      container.Register(Component.For<IContainerAccessor>().ImplementedBy<ContainerAccessor>()); 
      container.Resolve<IContainerAccessor>().Container = container; 

      return container; 
     } 
    } 

    public interface IRegistrationConvention 
    { 
     IWindsorContainer RegisterTypesUsingConvention(IWindsorContainer container, List<Type> assemblyTypes); 
    } 

    public class DefaultRegistrationConvention : IRegistrationConvention 
    { 
     /// <summary> 
     /// Register every service possible from the calling assembly with default singleton lifestyle 
     /// with the exception of ISomething Factory where the the ISomething GetSomething() where 
     /// Something that implements ISomething is registered with transient lifestyle 
     /// </summary> 
     public IWindsorContainer RegisterTypesUsingConvention(IWindsorContainer container, List<Type> assemblyTypes) 
     { 
      // Step 1: Factories installation. 
      // We register interfaces ending 'Factory' keyword like proxy (implementionless) factories. 
      var factoryServices = new List<Type>(); 
      var factorySelector = new FullNameFactorySelector(); 
      foreach (var factoryType in assemblyTypes.Where(t => t.Name.EndsWith("Factory") && t.IsInterface)) 
      { 
       foreach (var method in factoryType.GetMethods()) 
       { 
        if (method.Name.StartsWith("Get") == false) continue; 
        if (method.ReturnType.IsInterface == false) continue; 
        factoryServices.Add(method.ReturnType); 
       } 

       container.Register(Component.For(factoryType).AsFactory(factorySelector)); 
      } 

      // Step 2: Rest of the services registrations 
      // transientServices list is populated with services that needs to has transient lifespan 
      // everything else needs to go as preconfigured lifestyle - lifeStyleType 
      var components = assemblyTypes.Where(t => !t.IsInterface && !t.IsAbstract); 
      foreach (var component in components) 
      { 
       // for every interface and implementation do registration 
       foreach (var service in component.GetInterfaces()) 
       { 
        IRegistration registration; 
        Type service1 = service; 
        if (factoryServices.Any(x => x.FullName == service1.FullName)) 
        { 
         if (component.IsGenericType) 
         { 
          // GetInterfaces() and GetMethod().ReturnType both returns Type.FullName = null 
          // Castle.Windsor fails to Resolve later generic types if registered type is with FullName = null, 
          // Workaround is to find the type with correct FullName from the 'assemblyTypes' 
          var serviceWithFullName = assemblyTypes.FirstOrDefault(x => x.Name == service1.Name); 
          if (serviceWithFullName == null) continue; // if null then the mapping is not supported by this convention 
          registration = Component.For(serviceWithFullName) 
           .ImplementedBy(component) 
           .LifestyleTransient() 
           .Named(serviceWithFullName.FullName + "/" + component.FullName); 
         } 
         else 
         { 
          registration = Component.For(service) 
           .ImplementedBy(component) 
           .LifestyleTransient() 
           .Named(service.FullName + "/" + component.FullName); 
         } 
        } 
        else 
        { 
         registration = Component.For(service) 
          .ImplementedBy(component) 
          .Named(service.FullName + "/" + component.FullName) 
          .LifeStyle.Is(LifestyleType.Singleton); 

        } 
        container.Register(registration); 
       } 
      } 

      return container; 
     } 
    } 
} 

Все вышеперечисленное делает что замок Виндзор делает это уже модульным и расширяемым способом, не ограничивая возможности Castle Windsor.С нескольких строками он регистрирует все приложение следующих конвенций и позволяет добавлять конкретные конвенции, как для: Mvc, WebAPI, AutoMapper, ФОС, кварц и других

0

Есть несколько ConfigureX методов для этой цели, а именно ConfigureIf или типа на основе ConfigureFor<IDummyService> ,

Here's ссылка на соответствующую документацию.

 Смежные вопросы

  • Нет связанных вопросов^_^