IoC-Castle Windsor 2.1

Source: Internet
Author: User
I have found some articles on the Windsor tutorial. TerryLee has written a lot in the blog park, as well as some examples of codeproject, but they are not quite clear. Today, I saw a series written by Alex Henderson, which is very simple and clear. The following is the main content
Part 1-Simple configuration
Part 2-Array Configuration
Part 3-Dictionary configuration
Part 4-Switching configurations
Part 5-Configuration parameters
Part 6-Switching between lifestyles
Part 7-Switching implementations
Part 8-Referencing implementations by key
Part 9-Constructor Injection
Part 10-Setter Injection
Part 11-Factories
Part 12-Decorators
Part 13-Injecting Service Arrays
Part 14-Startable Facility


Basic example
The project references Castle. Core. dll, Castle. MicroKernel. dll, Castle. Windsor. dll, and namespace:
Using Castle. Windsor;
Using Castle. Windsor. Configuration. Interpreters;
Assume there is a service class TaxCalculator used to calculate the tax amount:

public class TaxCalculator{    private decimal _rate = 0.125M;    public decimal Rate    {        set { _rate = value; }        get { return _rate; }    }    public decimal CalculateTax(decimal gross)    {        return Math.Round(_rate * gross, 2);    }}

The code for calculating the tax amount is as follows:

WindsorContainer container = new WindsorContainer(new XmlInterpreter());TaxCalculator calculator = container.Resolve<TaxCalculator>();decimal gross = 100;decimal tax = calculator.CalculateTax(gross);Console.WriteLine("Gross: {0}, Tax: {1}", gross, tax);Console.ReadKey();

The configuration in app. config is as follows:

<configuration>    <configSections>        <section name="castle"            type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />    </configSections>
<castle> <components> <component id="taxcalc.service" type="Windsor.Test.TaxCalculator, Windsor.Test"> </component> </components> </castle></configuration>

Run the program and the computing result is 12.5. You can specify other values for the Rate attribute in the configuration file, for example:

<component id="taxcalc.service" type="Windsor.Test.TaxCalculator, Windsor.Test">    <parameters>        <Rate>0.25</Rate>    </parameters></component>

The above configuration specifies the tax rate of 0.25, so the calculation result is 25


Configuration
If the property to be injected when creating an object instance in the Windsor container is an array instead of a single value such as Rate, how can I configure it?
Assume that the property to be injected is:

public DateTime[] Holidays{    get { return _holidays; }    set { _holidays = value; }}

The configuration is as follows:

<component id="holidays.service" type="Windsor.Test.HolidayService, Windsor.Test" >    <parameters>        <Holidays>            <array>                <item>2007-12-24</item>                <item>2007-12-25</item>                <item>2008-1-1</item>            </array>        </Holidays>    </parameters></component>

If the property to be injected is of the Dictionary type, for example:

public Dictionary<string, string> Aliases{    get { return _aliases; }    set { _aliases = value; }}

The configuration is as follows:

<component id="aliases.service" type="Windsor.Test.HolidayService, Windsor.Test">    <parameters>        <Aliases>            <dictionary>                <entry key="dog">duck</entry>                <entry key="ate">broke</entry>                <entry key="homework">code</entry>            </dictionary>        </Aliases>    </parameters></component>

We do not need to initialize the injected array or Dictionary attribute to create this object. During the injection, Windsor creates an array or Dictionary object and sets it to the corresponding attribute.
If there are many services configured through Windsor, I have created two configurations, one for testing and the other for the production environment, how can we conveniently switch between the two configurations? You can use include in the configuration file,
Example:

<castle>    <!--<include uri="file://container-debug.config" />-->    <include uri="file://container-live.config" /></castle>

Include can even contain resources in assembly (Files embedded in assembly)
In addition, you can define attributes in the configuration file and then reference these attributes elsewhere, such as defining attributes:

<configuration>  <properties>    <myProperty>Live</myProperty>  </properties></configuration>

Usage attributes:

<component id="whatConfig.service" type="Windsor.Test.HolidayService, Windsor.Test">    <parameters>        <Configuration>#{myProperty}</Configuration>    </parameters></component>

We can configure multiple implementation methods for the same service and use id to obtain the object instances of each implementation method:

<component id="reader.file1" type="IoC.Tutorials.Part8.FileReader, IoC.Tutorials.Part8">    <parameters>        <FileName>file1.txt</FileName>    </parameters></component><component id="reader.file2" type="IoC.Tutorials.Part8.FileReader, IoC.Tutorials.Part8">    <parameters>        <FileName>file2.txt</FileName>    </parameters></component>

Then, use the id in the configuration to obtain the instance object:

WindsorContainer container = new WindsorContainer(new XmlInterpreter());FileReader defaultReader = container.Resolve<FileReader>();FileReader file1Reader = container.Resolve<FileReader>("reader.file1");FileReader file2Reader = container.Resolve<FileReader>("reader.file2");

We can use the container. Kernel. HasComponent (string key) method in the code to determine whether a specific key has registered a service.


Life Cycle, Lifecycle
The life cycle of an object in the Windsor container is as follows:
Singleton: Singleton Mode
Transient: temporary object mode. Each time a new object is created, it is returned to the requester.
PerThread: runs in singleton mode on the current execution thread.
Pooled: uses an object pool to manage Request objects and returns object instances from the object pool.
Custom: Implement Castle. MicroKernel. Lifestyle. ILifestyleManager or inherit from Castle. MicroKernel. Lifestyle. AbstractLifestyleManager to implement Custom object lifecycle management
By default, the life cycle of a component is in singleton mode. You can set it in the configuration file:

<component id="taxcalc.service" type="Windsor.Test.TaxCalculator, Windsor.Test" lifestyle="transient" />

You can also add attributes such as [Castle. Core. Transient] and [Castle. Core. PerThread] to the class to set the life cycle of the component and ignore the settings in the configuration file.
Windsor supports Castle. core. IInitializable and System. IDisposable interface. If the class implements the IInitializable interface, the container will execute the Initialize method of the interface after the object instance is created. If the class implements the IDisposable interface, the Dispose method will be executed when the object is destroyed.


Constructor Injection
The preceding example uses setter injection. The following example uses constructor injection.
There is an interface for string encoding, which has two implementations:

public interface IEncoder{string Encode(string source);}public class NullEncoder : IEncoder{public string Encode(string source){return source;}}public class SillyEncoder : IEncoder{    private char[] _mixedUp = "YACBDFEGIHJLKMONPRSQTUWVXZ".ToCharArray();
public string Encode(string source) { string upperSource = source.ToUpper(); char[] encoded = new char[source.Length]; for (int i = 0; i < encoded.Length; i++) { encoded[i] = MapCharacter(upperSource[i]); } return new string(encoded); }
private char MapCharacter(char ch) { if ((ch >= 'A') && (ch <= 'Z')) { return _mixedUp[ch - 'A']; } return ch; }}

Then there is a message sending class whose constructor requires an IEncode object:

public class MessageSender{    private readonly IEncoder _encoder;    private readonly string _from;    public MessageSender(string from, IEncoder encoder)    {        _from = from;        _encoder = encoder;    }    public void SendMessage(string to, string body)    {        Console.WriteLine("to: {0}\r\nfrom: {1}\r\n\r\n{2}", to, _from, _encoder.Encode(body));    }}

Use Windsor to automatically create an IEncoder object provided to MessageSender constructor. You must specify the value of the from parameter in the configuration file. Otherwise, the Windsor will throw an exception and cannot create
Create a MessageSender object
The code used is as follows:

WindsorContainer container = new WindsorContainer(new XmlInterpreter());MessageSender sender = container.Resolve<MessageSender>();sender.SendMessage("hammet", "castle is great!");Console.ReadKey();

The configuration is as follows:

<component id="encoder.silly"    service="Windsor.Test.IEncoder, Windsor.Test"    type="Windsor.Test.SillyEncoder, Windsor.Test" /><component id="encoder.null"    service="Windsor.Test.IEncoder, Windsor.Test"    type="Windsor.Test.NullEncoder, Windsor.Test" /><component id="messageSender"    type="Windsor.Test.MessageSender, Windsor.Test">    <parameters>MessageSender        <from>alex@bittercoder.com</from>    </parameters></component>

We have two IEncoder implementations above. We can specify which implementation class to use for the MessageSender constructor in the configuration file:

<component id="messageSender"    type="Windsor.Test.MessageSender, Windsor.Test">    <parameters>MessageSender        <from>alex@bittercoder.com</from>        <encoder>${encoder.null}</encoder>    </parameters></component>


Factory Facilities
The classes we write are completely controlled by ourselves, so we can allow them to manage through the Windsor container. However, for some third-party service programs, constructor may have additional dependencies, in this way, we cannot directly use the Windsor container for management through configuration. In this case, we can use the Factory Facilities of Windsor to implement a Factory and tell Windsor to use our Factory to create a specific service object instance.
For example, we have implemented the following factory class to create an ISMS service object:

public class SmsServiceFactory{    private string _userName;    private string _password;    private int _retryAttempts = 3;
public SmsServiceFactory(string userName, string password) { _userName = userName; _password = password; } public int RetryAttempts { get { return _retryAttempts; } set { _retryAttempts = value; } }
public ISmsService CreateService() { SmsService service = new SmsService(); SmsService.SmsConfig config = new SmsService.SmsConfig(); config.SetCredentials(_userName, _password); config.RetryAttempts = _retryAttempts; service.SetConfig(config); return service; }}

Then we use the following configuration to specify the Factory class we use through Windsor Factory Facilities:

<castle>    <facilities>        <facility            id="factorysupport"            type="Castle.Facilities.FactorySupport.FactorySupportFacility, Castle.MicroKernel" />    </facilities>    <components>        <component id="smsService.Factory"            type="Windsor.Test.SmsServiceFactory, Windsor.Test">            <parameters>                <userName>joe</userName>                <password>secret</password>            </parameters>        </component>        <component id="smsService.default"            type="Windsor.Test.ISmsService, Windsor.Test"            factoryId="smsService.Factory"            factoryCreate="CreateService" />    </components></castle>

The code used is the same as other examples:

WindsorContainer container = new WindsorContainer(new XmlInterpreter());ISmsService smsService = container.Resolve<ISmsService>();smsService.SendMessage("+465556555", "testing testing...1.2.3");

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.