SConfiguration file is required to use S2Container. Configuration file contains blue print of how components are to be built. Configuration file is a XML document with file extension dicon.
DOCTYPE may not be omitted. When creating a dicon file, cut and paste the above sample. Root element is a component. Each component is defined by a component tag and class name is defined in a class attribute of a component tag. Component name is defined in a name attribute of a component. Refer to S2Container Definition Reference for further details.
Configuration file may be specified by embedding it in resource or by using the file system.
To embed the configuration file, argument path specifies complete path name including the namespace. A slash (/ character is used as a eparator between folders. Configuration file build action property should be set to the embedded resource.
In C#, when a project is created, namespace is also created. In VB.NET, however, a namespace is not created when a VB.NET project is created. For example, if project folder is /Sample/Logic/aaa.dicon, C# set the default namespace /Sample/Logic/aaa.dicon to the project. In VB.NET, however, the namespace is the default namespace /aaa.dicon.
To specify using a file system, absolute or relative file path to the configuration file is specified.
Configuration file may also be specified by a combination of embedded and file system. When there is a conflict between the choice, file system specification takes precedence.
Following method is used to get a component from the S2Container:
- Argument to S2.NET.Framework.Container.IS2Container#GetComponent(object componentKey)
is either a System.Type which represents component type declaration or a component name. Refer to component tag for further information. Component class where instanceof class is true may be gotten from the container. The class, however, should be associated with an unique component in the S2Container. If there are several components of the class, TooManyRegistrationRuntimeException is raised because S2Container can not determine which component to get.
Similarly, component name should be unique in the S2Container. Specifying non-unique component name will raise a TooManyRegistrationRuntimeException.
Example: Following is an example to get a component by specifying a class:
<s2.net> section may be added to the application configuration file (not a dicon file).
<s2.net> session specifies root configuration file (dicon file) and assembly to load when S2Container is created.
Specified configuration file is set to the root element when S2.NET.Framework.Container.Factory.SingletonS2ContainerFactory#Init in invoked.
Specified assembly is read in to an application domain when S2.NET.Framework.Container.Factory.S2ContainerFactory#Create is invoked.
<configuration>
<configSections>
<section name="s2.net"
type="S2.NET.Framework.Xml.S2SectionHandler, s2dotnet" />
</configSections>
<s2.net>
<!-- SingletonS2ContainerFactory#Init sets specified dicon file
as the initial dicon file -->
<configPath>App.dicon</configPath>
<!-- Set assemblies specified when creating an S2Container
into AppDomain (S2ContainerFactory#Create) -->
<assemblys>
<assembly>Assembly name1</assembly>
<assembly>Assembly name2</assembly>
</assemblys>
</s2.net>
</configuration>
There are three types of Dependency Injection: (1) Constructor injection which sets component constructs by means of a constructor argument, (2) Property injection which sets using set accessor of a property (3) Method injection which sets using an initialize method. Method injection is a dependency injection introduced by S2. s2dotnet supports all three types of dependecy injection as well as their hybrid.
Constructor injection applies dependency injection to an arbitrary argument in a constructor.
Following items are entered in the S2Container configuration file:
Component specification Component is specified by a component tag. class attribute is used to specify a class name. name attribute is used to specify a component name.
Constructor arguments Arguments to be passed to a component constructor is specified as values to arg tags. When the value is a string, it is surrounded by double quotations (".
Property injection uses set accessor of a property. Following items are specified in the S2Container configuration file:
Component Specification Component specification is similar to constructor injection. name attribute may be used to assign a name to a component.
Setting a property Component property is specified by a property tag that is a child of the component tag. Property name is specified by a name attribute.
Method injection invokes a method and applieds dependency injection.
Following items are specified in the S2Container configuration file.
Component specification Component specification is the same as constructor injection.
Component may be assigned a name by using name attribute.
Specify initialization method. Method in a component is invoked by specifying initMethod tag. name attribute is used to specify the name of the method to invoke. Arguments to the method are specified in the arg child tags. name attribute may be omitted and JScript.NET may be used instead. In this situtation, the component itself is specified by self.
If all configuration are written in a single file, the file soon becomes very large. S2Container allows developers to divide the configuration file into several files and supports include clause to logically construct one configuration file from these files. Following is an example of including content of bar.dicon file:
File to include is specified in the path attribute of a include tag. Refer to include tag for detailed information. Component are searched in the following order: (1) In the component where the component is registered (2) In child S2Container in order of the include statement. In the following components definition, "Foo" is searched first, followed by "aaa.dicon" and lastly by "bbb".
When automatically using constructor injection and setter injection, it is possible to automatically inject components in configuration to be included. Refer to automatic binding for conditions to automatically inject dependencies.
Following is an example to show the order of component search. Property of a component to set by setter injection (HelloImpl) is defined in both included configuration files aaa.dicon and bbb.dicon. Let's find out which component HelloClient will use.
namespace S2.NET.Examples.Reference.Includes.Impl
{
public class HelloImpl : IHello
{
private string helloMessage;
public HelloImpl() {}
public string Message
{
get { return this.helloMessage; }
set { this.helloMessage = value; }
}
}
}
HelloImpl only defines Message property. Following are result of invoking showMessage() method in each HelloClient.
Result of RootHelloClient
Hello Aaa!
S2Container will first try to find HelloImpl in root.dicon. Since HelloImpl is not defined in root.dicon, S2Container will next try to find it in the first included file, aaa.dicon. Since HelloImpl is defined in aaa.dicon, this definition is used and "Hello Aaa!" is outputted.
Result of aaa
Hello Aaa!
aaa used a component defined in aaa.dicon.When injection is automatically done, child S2Container is unable to use component defined in the parent S2Container. For example, even if HelloImpl is defined in root.dicon, it will not be automatically injected into aaa.
Result of bbb
Hello Bbb!
Similar to the case with aaa, bbb uses HelloImpl component defined in bbb.dicon.
Files in this example are available in the S2/NET/Examples/Reference/Includes in S2.NET.Examples project.
Namespace is used to distinguish component definitions from several configuration file. Namespace is specified by namespace attribute of a components tag.
Namespace is not necessary to require to reference components in the same S2Container. To reference components in other S2Container, however, namespace prefix associated with the S2Container is prepended to the component name. For example, foo.aaa and bar.aaa have the same component name "aaa" but have different namespace prefix. Thus, they are treated as two different components.
It is a convention to name the configuration file namespace.dicon.
instance attribute of a component tag specifies how component instance is managed by a S2Container. Default is singleton which implies IS2Containter.GetComponent() method always gets the same component. To get a new component each time IS2Container.getComponent() method is invoked, set the instance attribute to prototype. To manage component based on request (System.Web.HttpRequest), set the instance attribute to request. To manage based on a session, set the instance attribute to session.
When using Seasar framework with a presentation framework, there may sometimes be a need to set an instance created by a presentation framework to a component managed by S2Container. In this kind of circumstances where Dependency Injection to a component outside the S2Container is necessary, the following method are used.
Specify external component in the first argument and external component class or component name in the second argument. Also, set instance attribute to outer in S2Container definition.
instance attribute
Description
singleton (default)
Same instance is returned on every invocation of IS2Container.GetComponent().
prototype
New instance is returned on each invocation of IS2Container.GetComponent().
request
One instance is created for each request. A component is saved to a request using the name set to the name attribute. S2HttpModule must be set when using request.
session
One instance is created for each session. A component is saved to a session using the name set to the name attribute. S2HttpModule must be set when using session.
outer
Instance of a component is created outside of S2Container. Only dependency is injected. Aspect and Constructor injection can not be applied.
Using initMethod and destroyMethod, component lifecycle may also be managed by a container. Method specified by initMethod tag is invoked when S2Container is initialized (S2Container.Init()) and method specified by destroyMethod tag is invoked when S2Container is destroyed (S2Container.Destroy()). initMethod is executed in order as they are defined in the container configuration and destroyMethod is executed in reverse order as they are defined in the container configuration. When instance attribute is not "singleton", destroyMethod is ignored. Following is an example to set aaa to 111 on initialization and set aa to null termination to System.Collections.Hashtable#Add() method.
By default, dependency between container is automatically resolved when type is an interface. It is also possible to specify autoBinding attribute in a component tag.
autoBinding
Description
auto(default)
Is an argument is not specified and type is an interface,it is automatically bound. Default values are set to arguments whose type is not an interface. If default value is not available, an argument is set to null. If a property is explicitly specified, it is used. If property is not specified and property type is an interface, it is automatically bound.
constructor
If an argument to a constructor is explicitly specified, that argument is used. Is an argument is not specified and type is an interface,it is automatically bound. Default values are set to arguments whose type is not an interface. If default value is not available, an argument is set to null. If a property is explicitly specified, it is used.
property
If an argument to a constructor is explicitly specified, that argument is used. If there is no specification, default constructor is used. Type interface property is automatically bound.
none
If an argument to a constructor is explicitly specified, that argument is used. If a property is explicitly specified, it is used.
Component should not depend on a S2Container but there are some situations where a component may need to invoke S2Container method. S2Container is defined with a name "container" so is the value of arg or property tag is set to "container" it is possible to get an instance of a container. Furthermore, it is also possible to automatically bind by defining a S2Container type setter method. Following are examples of using arg and property tag to specify "container".
When using .NET Framework from an application, S2Container is explictly created. However, when S2Container is required from an web application, following class is used to create a S2Container:
- S2.NET.Framework.Container.Web.S2HttpModule
To use S2HttpModule, following items may be enteredin the Web.config file. Sample Web.config file is available in S2.NET/Framework/Container/Web/Web.config.
Path of root S2Container configuration is specified in the configPath element in s2.net section in the Application Configuration File. After starting S2HttpModule, S2Container instance may be acquired by the following method:
As a convention, root S2Container file is named app.dicon. It is also a convention to not define any component in app.dicon file. Resources that uses the default namespace is entered in this file.
<components>
<include path="Examples/foo.dicon"/>
<include path="Examples/bar.dicon"/>
<!-- include all second level configuration file -->
</components>
AOP may be applied to a component. Following is an example of applying TraceInterceptor to a Date class. The current s2dot.net restricts usage of AOP to class extending System.MarshalByRefObject or an interface receiving a component. Following is an example to apply TraceInterceptor to an ArrayList:
Interceptor name is specified as a value to aspect tag. Multiple method names and property names may be specified in a pointcut attribute by delimiting with a comma. If a pointcut attribute is not specified, all methods in an interface implemented by component will be implied. Regular expression (System.Text.RegularExpressions.Regex) may be used to specify an method name. Following is an example of a configuration.
private const string PATH = "Framework/Container/Factory/AspectTagHandlerTest.dicon";
[Test]
public void TestAspect()
{
IS2Container container = S2ContainerFactory.Create(PATH);
IList list = (IList) container.GetComponent(typeof(IList));
int count = list.Count;
IFoo foo = (IFoo) container.GetComponent(typeof(IFoo));
int time = foo.Time;
foo.ToString();
int hashCode = foo.GetHashCode();
}
public interface IFoo
{
int Time { get; }
int GetHashCode();
string ToString();
}
public class FooImpl : IFoo
{
private int time_ = 3;
public FooImpl()
{
}
#region IFoo member
public int Time
{
get
{
return time_;
}
}
public override int GetHashCode()
{
return base.GetHashCode();
}
public override string ToString()
{
return time_.ToString();
}
#endregion
}
Result is as follows:
BEGIN System.Collections.ICollection#get_Count()
END System.Collections.ICollection#get_Count() : 0
BEGIN TestS2.NET.Framework.Container.Factory.AspectTagHandlerTest+IFoo#get_Time()
END TestS2.NET.Framework.Container.Factory.AspectTagHandlerTest+IFoo#get_Time() : 3
BEGIN TestS2.NET.Framework.Container.Factory.AspectTagHandlerTest+IFoo#GetHashCode()
END TestS2.NET.Framework.Container.Factory.AspectTagHandlerTest+IFoo#GetHashCode() : 1168
meta data may be applied to components, component, arg, and property tags. meta tag is specified as a child element to the element which is to have a meta tag. Following is an example of specifying a meta tag to a components tag.
Meta data information specified in components, component, arg, and property tags may be retrieved using the following methods defined in IS2Container, IComponentDef, IArgDef, and IPropertyDef.
HttpRequest may be automatically bound to a component by defining HttpRequestRequest { set; } to a component and defining S2HttpModule in Web.config file. S2Container will automatically set the request.
Likewise, HttpServletResponse and HttpSession may also be automatically bound by just defining a property (set accessory). Furthermore, by using S2HttpModule, HttpRequest, HttpResponse, HttpSessionState, and HttpApplication may respectively be accessed as a component named request, response, session, and httpApplication.
Path of configuration file may be specified. Path of a configuration file may be specified by embedded resource or by a file system. When specifying by embedded resource, complete namespace must be included using slash (/ as a separator character. Configuration build action property should be set to the embedded resource. C# project creates a namespace when a folder is created, but VB.NET does not. For example, if project folder is /Sample/Logic/aaa.dicon, C# project assignes /Sample/Logic/aaa.dicon as a default namespace, but a default name space in VB.NET is /aaa.dicon.
When using a file system, absolute or relative file path is specified. Configuration file may also be specified by a combination of embedded and file system. When there is a conflict between the choice, file system specification takes precedence. It is necessary to specify before the component tag.
Specify full path of a class. If JScript.NET expression is used as a value of a tag to specify a component, class attribute may be omitted. If class attribute is specified when JScript.NET expression is used, it will be type checked.
Specify how S2Container manages instance of a component. Possible values are singleton (default), prototype, outer, request, and session. Refer to instance management for further information.
Specify how S2Container resolves dependencies between components. Possible values are auto (default), constructor, property, and none. Refer to automatic binding for further information.
If specified as a child tag to a component tag, will be used as arguments to a contructor. Arguments are passed to a constructor in the order they are specified.
If specified as a child tag to a initMethod tag or a destroyMethod tag, will be used as arguments to a method. Arguments are passed to a constructor in the order they are specified. Value of a property is specified as a value to JScript.NET expression or in a child component tag.
Written as a child tag to component tag. Arguments are specified in the arg child tags. Method in a component may be invoked without specifying a name attribute by using JScript.NET expression. Only has meaning when used in self representing initMethod tag where component tag itself is defined, out representing Console.Out, err representing Console.Error.
s2dotnet must be set up before trying examples in this section. S2.NET.Examples project will be created as a Windows application. The example may be executed by setting S2.NET.Examples project as a startup project and selecting the F5 key.
This example will use constructor injection to display a message. Following files are created in this example:
Interface (IHello.cs)
Implementation class (HelloConstructorInjection.cs)
dicon file (HelloConstructorInjection.dicon)
Execution class (HelloconstructorInjectionClient class in HelloConstructorInjection.cs)
Create an interface
Define ShowMessage()
Interface is defined first. Developers are able to design on interfaces without knowing the actual implementation. Furthermore, a Mock may be used instead of the actual implementation during the unit test.
using System;
using S2.NET.Framework.Container;
using S2.NET.Framework.Container.Factory;
namespace S2.NET.Examples.Reference.Injection
{
public class HelloConstructorInjectionClient
{
private static readonly String PATH =
"S2/NET/Examples/Reference/Injection/HelloConstructorInjection.dicon";
public void Main()
{
// when getting a component by specifying a type
IS2Container container = S2ContainerFactory.Create(PATH);
IHello hello = (IHello) container.GetComponent(typeof(IHello));
hello.ShowMessage();
// when getting a component by specifying a name
IHello hello2 = (IHello) container.GetComponent("hello");
hello2.ShowMessage();
}
}
}
Result
Value of the arg tag should be properly displayed as follows:
Hello World!
Hello World!
Files in this example is available in the S2/NET/Examples/Reference/Injection folder in the S2.NET.Examples project.
This example displays a message using property injection. Following files are created in the example:
Interface (IHello.cs) (same as in the constructor injection)
Implementation class (HelloPropertyInjection.cs)
dicon file (HelloPropertyInjection.dicon)
Implementation class (HelloPropertyInjection.cs中のHelloPropertyInjectionClient class)
Create an interface
Define ShowMessage()
Interface is the same as in the constructor injection example. Definition of get assessor for a property and set assessor are not required because whether to use constructor or set assessor is a matter of implementation.
AddMessage(string message) appends a message several times and ShowMessage() displays the message.
using System;
using System.Text;
namespace S2.NET.Examples.Reference.Injection
{
public class HelloMethodInjection : IHello
{
private StringBuilder builder = new StringBuilder();
public HelloMethodInjection() {}
public void AddMessage(string message)
{
this.builder.Append(message);
}
public void ShowMessage()
{
Console.WriteLine(this.builder.ToString());
}
}
}
Create dicon file
Define a component using component tags
Set a value to arguments of a method by specifying initMethod that is a child of a component tag.
Set AddMessage(string message) to "Hello" in arg tag
Specify AddMessage(string message) to "World!" using JScript.NET expression
Define a constructor with IDictionary as an argument
Implement ShowMessage()
Constructor receives IDictionary and ShowMesssage() searches IDictionary with key "hello" and displays the result.
using System;
using System.Collections;
namespace S2.NET.Examples.Reference.AutoBinding
{
public class AutoHelloConstructorInjection : IHello
{
private IDictionary dictionary;
public AutoHelloConstructorInjection(IDictionary dictionary)
{
this.dictionary = dictionary;
}
public void ShowMessage()
{
Console.WriteLine(dictionary["hello"]);
}
}
}
Create dicon file
Define System.Collections.Hashtable, which is an implementation of System.Collections.IDictionary, as a component using component tags.
In initMethod that is a child tag to a component tag, set Hashtable key to "hello" and value to "Hello World!"
Define AutoHelloConstructorInjection as a component using component tags. In this example, "auto" is explicitly specified but since this is the default autoBinding attribute value, it is usually not explicitly specified.
Note that AutoHelloConstructorInjection does not have a arg tag. When using constructor injection, it is normally necessary to define arguments using arg tags but when implementation class of IDictionary is defined in S2Container, S2Container will search within itself and automatically set the arguments. However, if the property type is not an interface, automatic binding does not occur.
Set assessor in IDictionary Message receives IDictionary and ShowMessage() uses key value "hello" to get and display a string from IDictionary.
using System;
using System.Collections;
namespace S2.NET.Examples.Reference.AutoBinding
{
public class AutoHelloPropertyInjection : IHello
{
private IDictionary dictionary;
public AutoHelloPropertyInjection()
{
}
public IDictionary Message
{
set { dictionary = value; }
}
public void ShowMessage()
{
Console.WriteLine(dictionary["hello"]);
}
}
}
Create dicon file
Define System.Collections.Hashtable, which is an implement clas of System.Collections.IDictionary, as a component using component tags.
In initMethod that is a child tag to a component tag, set Hashtable key to "hello" and value to "Hello World!"
Define AutoHelloPropertyInjection as a component using component tags. In this example, "auto" is explicitly specified but since this is the default autoBinding attribute value, it is usually not explicitly specified.
Note that AutoHelloPropertyInjection does not have a property tag. When using property injection, it is normally necessary to define a property using property tags but when implementation class of IDictionary is defined in S2Container, S2Container will search within itself and automatically set the property. However, if the property type is not an interface, automatic binding does not occur.