Kamil Kliczbor @ asptip.net

17Dec/130

Castle Windsor – IContributeComponentModelConstruction

Castle Windsor

Wprowadzenie

Szczerze powiedziawszy, ciężko jest określić polską nazwę dla interfejsu IContributeComponentModelConstruction. Powiedzmy, że na potrzebę tego wpisu będę się posługiwać sformułowaniem inspektor. A w samym wpisie opiszę czym jest i jak używać inspektorów.

Inspektor

Klasa implementująca interfejs IContributeComponentModelConstruction ma wgląd w metadane komponentów. Taki inspektor według dedykowanej logiki może zmienić konfigurację zarejestrowanego serwisu. Sam Castle posiada kilka wbudowanych inspektorów, które związane są m.in. z proxy, zależnościami, czy zarządzaniem trybem życia komponentów. Implementacje tego interfejsu uruchamiane są zaraz po zebraniu metadanych odnośnie zarejestrowanego komponentu.

Nie zaleca się modyfikowania wartości metadanych instancji ComponentModel poza inspektorami.

Definicja interfejsu jest następująca:

namespace Castle.MicroKernel.ModelBuilder
{
  /// <summary>
  /// Implementors must inspect the component for a given information or parameter.
  /// 
  /// </summary>
  public interface IContributeComponentModelConstruction
  {
    /// <summary>
    /// Usually the implementation will look in the configuration property
    ///               of the model or the service interface, or the implementation looking for
    ///               something.
    /// </summary>
    /// <param name="kernel">The kernel instance</param><param name="model">The component model</param>
    void ProcessModel(IKernel kernel, ComponentModel model);
  }
}

Widzimy, że interfejs eksponuje jedną metodę, która przyjmuje parametry o typach IKernel oraz ComponentModel.

Podpięcie dedykowanego inspektora polega na dodaniu własnej implementacji do kolekcji inspektorów:

container.Kernel.ComponentModelBuilder.AddContributor(new MyContributor());

Przykład

Troszkę ciężko jest wymyślić jakiś sensowny przykład bez kontekstu, który będzie ładnie potrafił ukazać działanie przykładowego inspektora.
Widok na solution:

WindsorContributors

Powiedzmy, że będziemy chcieli umieć zmienić tryb życia komponentu w zależności czy zostanie on oznaczony specjalnym atrybutem. Nazwijmy ten atrybut TransientAttribute. Jego definicja jest prosta, a sam atrybut służy jedynie jako "marker" do oznaczania klas:

using System;
namespace WindsorContributors
{
    public class TransientAttribute : Attribute
    {
    }
}

Zdefiniujmy dwa serwisy,  które będą użyte w tym projekcie. Jeden z nich będzie miał cykl życia Singleton, natomiast cykl durugiego ulegnie zmianie przez wpięcie naszego kontrybutora. Jak widać w drugim przypadku SingletonChangedService jest oznaczony atrybutem Transient.

namespace WindsorContributors
{
    public class SingletonService
    {
    }

    [Transient]
    public class SingletonChangedService
    {
    }
}

Definicja Instalatora zależności zakłada rejestrację komponentów z trybem życia Singleton (domyślnie Windsor Castle ustawia taki tryb, jeżeli nie został on explicite wskazany):

using Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Windsor;

namespace WindsorContributors
{
    public class WindsorInstaller : IWindsorInstaller{
        public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            container.Register(
                Component.For<SingletonService>(),
                Component.For<SingletonChangedService>()
                );
        }
    }
}

No i wreszcie samo mięsko: inspektor sprawdza tryb życia i istnienie atrybutu Transient. Jeżeli te warunki są spełnione, to komponent otrzymuje tryb życia Transient.

using Castle.Core;
using Castle.MicroKernel;
using Castle.MicroKernel.ModelBuilder;

namespace WindsorContributors
{
    public class TransientAttributeInspector : IContributeComponentModelConstruction
    {
        public void ProcessModel(IKernel kernel, ComponentModel model)
        {
            if (model.LifestyleType != LifestyleType.Transient)
            {
                var hasAttribute = model.Implementation.GetCustomAttributes(typeof (TransientAttribute), false).Length > 0;
                if (hasAttribute)
                {
                    model.LifestyleType = LifestyleType.Transient;
                }
            }            
        }
    }
}

Główne wejście programu. Zwróćmy uwagę na kolejność w jakiej rejestrujemy inspektor oraz wskazujemy na instalator zależności.

using System;
using Castle.Windsor;

namespace WindsorContributors
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var container = new WindsorContainer())
            {
                container.Kernel.ComponentModelBuilder.AddContributor(new TransientAttributeInspector());
                container.Install(new WindsorInstaller());
                var singleton1 = container.Resolve<SingletonService>();
                var singleton2 = container.Resolve<SingletonService>();
                var transient1 = container.Resolve<SingletonChangedService>();
                var transient2 = container.Resolve<SingletonChangedService>();

                Console.WriteLine("Wartości singleton1 i singleton2 są równe: {0}", singleton1.Equals(singleton2));
                Console.WriteLine("Wartości transient1 i transient2 są równe: {0}", transient1.Equals(transient2));
                Console.ReadKey();
            }
        }
    }
}

W rezultacie otrzymujemy następujący output z konsoli. Można by rzec w ujęciu matematycznym CND.

Wartości singleton1 i singleton2 są równe: True
Wartości transient1 i transient2 są równe: False

Podsumowanie

Jednym z głównych i ciekawych spostrzeżeń jest to, że należy w pierwszej kolejności wskazać inspektory, a następnie zainstalować komponenty. W przeciwnym razie, nasz inspektor nie zostanie wywołany, a oczekiwany rezultat nie będzie otrzymany.

Innym spostrzeżeniem jest to, że inspektory mają dostęp do metadanych zarejestrowanych komponentów. W inspektorze nie mamy dostępu do instancji, ale tylko metadanych rejestrowanych komponentów.

Bibliografia

Głównie ze strony Castla: http://docs.castleproject.org/Windsor.ComponentModel-construction-contributors.ashx

Comments (0) Trackbacks (0)

No comments yet.


Leave a comment

No trackbacks yet.