SPRNET-1536 merge SPRNET-CODECONFIG refdocs

This commit is contained in:
Steve Bohlen
2013-01-07 19:05:17 -05:00
parent 9b50c40a6b
commit 805d384d0d
8 changed files with 2718 additions and 484 deletions

View File

@@ -0,0 +1,574 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter version="5" xml:id="codeconfig-attribute-reference"
xmlns="http://docbook.org/ns/docbook"
xmlns:ns5="http://www.w3.org/1998/Math/MathML"
xmlns:ns42="http://www.w3.org/2000/svg"
xmlns:ns4="http://www.w3.org/1999/xlink"
xmlns:ns3="http://www.w3.org/1999/xhtml"
xmlns:ns="http://docbook.org/ns/docbook">
<title>Attribute Reference</title>
<para>In this chapter, we will explore the attributes that are the essential
components for declaring Object Definitions with Spring CodeConfig. These
attributes have a corresponding representation in Spring XML so should be
very familiar to current Spring.NET users.</para>
<sect1 xml:id="attribute-reference">
<title>[Configuration] Attribute</title>
<para>The <literal><link
linkend="configuration-attribute-reference">[Configuration]</link></literal>
attribute is applied to classes that contain one or more <literal><link
linkend="objectdef-attribute-reference">[ObjectDef]</link></literal>
attributed-methods. During scanning by the
<literal>CodeConfigApplicationContext</literal>, only types having the
<literal><link
linkend="configuration-attribute-reference">[Configuration]</link></literal>
attribute will be considered candidates to contain Object Definition
configurations.</para>
<sect2>
<title>Using the Configuration Attribute</title>
<para></para>
<sect3>
<title>Simple Usage</title>
<para>The most common usage of the <literal><link
linkend="configuration-attribute-reference">[Configuration]</link></literal>
attribute simply applies the attribute to a class without any
attribute parameters. The name of the of the type, once registered
with the <literal>CodeConfigApplicationContext</literal>, will be the
name of the class itself. Note that it is not a common use-case to ask
the container for this configuration class.<programlisting
language="csharp">[Configuration]
public class MyConfigurationClass
{
//[ObjectDef] methods here
}</programlisting></para>
</sect3>
<sect3>
<title>Controlling the Name of the Configuration Class</title>
<para>While not a common use-case, if you need fine-grained control
over the name of the type registered with the
<literal>CodeConfigApplicationContext</literal>, the <literal><link
linkend="configuration-attribute-reference">[Configuration]</link></literal>
attribute accepts a <literal>Name</literal> parameter which will be
applied to the registered Object Definition in the
<literal>CodeConfigApplicationContext</literal>.<programlisting
language="csharp">[Configuration("MySpecialConfigurationClass")]
public class MyConfigurationClass
{
//[ObjectDef] methods here
}</programlisting></para>
</sect3>
</sect2>
</sect1>
<sect1 xml:id="objectdef-attribute-reference">
<title>[ObjectDef] Attribute</title>
<para>The <literal><link
linkend="objectdef-attribute-reference">[ObjectDef]</link></literal>
attribute is applied to one or more methods within any class to which the
<literal><link
linkend="configuration-attribute-reference">[Configuration]</link></literal>
attribute has been applied. During scanning, any <literal>public
virtual</literal> method having the <literal><link
linkend="objectdef-attribute-reference">[ObjectDef]</link></literal>
attribute is considered to contain Object Definition metadata.</para>
<sect2>
<title>Using the ObjectDef Attribute</title>
<para></para>
<sect3>
<title>Simple Usage</title>
<para>The first usage of the <literal><link
linkend="objectdef-attribute-reference">[ObjectDef]</link></literal>
attribute simply applies the attribute to the method. The name of the
of the Object to be registered with the
<literal>CodeConfigApplicationContext</literal>, will be the name of
the method itself.<programlisting language="csharp">[Configuration]
public class MyConfigurationClass
{
[ObjectDef]
public virtual HomeController HomeController()
{
return new HomeController();
}
}</programlisting></para>
</sect3>
<sect3>
<title>Controlling the Name and Aliases of the Defined Object</title>
<para>If you need to control the name of the Object registered with
the <literal>CodeConfigApplicationContext</literal>, the
<literal><link
linkend="objectdef-attribute-reference">[ObjectDef]</link></literal>
attribute accepts one or more comma-delimited names as aliases for the
Object when registered.</para>
<programlisting language="csharp">[Configuration]
public class MyConfigurationClass
{
[ObjectDef(Names="TheHomeController")]
public virtual HomeController HomeController()
{
return new HomeController();
}
[ObjectDef(Names="TheSpecialNameForAboutController,AliasForAboutController")]
public virtual AboutController AboutController()
{
return new AboutController();
}
}</programlisting>
</sect3>
<sect3>
<title>Setting an Init Method for the Object</title>
<para>If you need to declare an Initialization Method for the object
definition, the <literal><link
linkend="objectdef-attribute-reference">[ObjectDef]</link></literal>
attribute accepts a method name to be invoked at the appropriate stage
in the object's creation by the
<literal>CodeConfigApplicationContext</literal>.</para>
<programlisting language="csharp">[Configuration]
public class MyConfigurationClass
{
[ObjectDef(InitMethod="AfterCreation")] //assumes a public method on the HomeController type named 'AfterCreation()'
public virtual HomeController HomeController()
{
return new HomeController();
}
}</programlisting>
<para><note>
<para>You can just also call the method 'AfterCreation' inside the
body of the HomeController implementation itself.</para>
</note></para>
</sect3>
<sect3>
<title>Setting a Destroy Method for the Object</title>
<para>If you need to declare a Destroy Method for the object
definition, the <literal><link
linkend="objectdef-attribute-reference">[ObjectDef]</link></literal>
attribute accepts a method name to be invoked at the appropriate stage
in the object's destruction by the
<literal>CodeConfigApplicationContext</literal>.</para>
<programlisting language="csharp">[Configuration]
public class MyConfigurationClass
{
[ObjectDef(DestroyMethod="CleanupResources")] //assumes a public method on the HomeController type named 'CleanupResources()'
public virtual HomeController HomeController()
{
return new HomeController();
}
}</programlisting>
</sect3>
</sect2>
</sect1>
<sect1 xml:id="dependson-attribute-reference">
<title>[DependsOn] Attribute</title>
<para>The <literal>[DependsOn]</literal> attribute can be applied to any
<literal><link
linkend="objectdef-attribute-reference">[ObjectDef]</link></literal>-attributed
method to declare a construction sequence dependency upon one or more
objects that the <literal>CodeConfigApplicationContext</literal> will
ensure are created prior to the creation of the current object. This is
only required if there is a hidden depedency between the two classes that
isn't exposed via constructor or setter properties.</para>
<sect2>
<title>Using the DependsOn Attribute</title>
<para></para>
<sect3>
<title>Controlling Creation Dependencies</title>
<para>The <literal>[DependsOn]</literal> attribute accepts one or more
comma-delimited strings representing the names of the objects upon
which the current object will depend.</para>
<programlisting language="csharp">[Configuration]
public class MyConfigurationClass
{
[ObjectDef]
[DependsOn("Dependency1")] //HomeController requires "Dependency1" to be created first
public virtual HomeController HomeController()
{
return new HomeController();
}
[ObjectDef]
[DependsOn("Dependency1", "Dependency2")] //AboutController requires "Dependency1" and "Dependency2" to be created first
public virtual AboutController AboutController()
{
return new AboutController();
}b
}</programlisting>
</sect3>
</sect2>
</sect1>
<sect1 xml:id="import-attribute-reference">
<title>[Import] Attribute</title>
<para>The <literal>[Import]</literal> attribute allows you to identify one
or more additional types that will also be scanned when the current type
is scanned.</para>
<sect2>
<title>Using the Import Attribute</title>
<para></para>
<sect3>
<title>Specifying Additional Types to Scan</title>
<para>To support your specifying additional types to scan, the
<literal>[Import]</literal> attribute accepts one or more
comman-delimited Types to scan as well. To be candidates for scanning,
the types listed in this array must also have the <literal><link
linkend="configuration-attribute-reference">[Configuration]</link></literal>
attribute applied to them. Note that no error is reported if these
types lack the<literal>[Configuration</literal>] attribute, but
without it they will not satisfy the scanner's requirements for
Configuration candidates.</para>
<programlisting language="csharp">[Configuration]
[Import(typeof(MySecondConfiguration), typeof(MyThirdConfiguration))]
public class MyConfigurationClass
{
[ObjectDef]
public virtual HomeController HomeController()
{
return new HomeController();
}
}
[Configuration]
public class MySecondConfiguration
{
[ObjectDef]
public virtual AboutController AboutController()
{
return new AboutController();
}
}
[Configuration]
public class MyThirdConfiguration
{
[ObjectDef]
public virtual SomeOtherController SomeOtherController()
{
return new SomeOtherController();
}
}</programlisting>
</sect3>
<sect3>
<title>Chaining [Import] Directives</title>
<para>Types pointed to by one <literal>[Import]</literal> attribute
may in turn have their own <literal>[Import]</literal> attributes
pointing to yet more types to scan. Using this approach, its possible
to specify perhaps only a single 'root' class from which to 'begin'
the scan and leverage the <literal>[Import]</literal> attribute to
'chain' successive types into the scanning scope, transitively
pointing from one <literal><link
linkend="configuration-attribute-reference">[Configuration]</link></literal>-attributed
type to the next as in the following example where
<literal>MyConfigurationClass</literal> has an
<literal>[Import]</literal> attribute pointing to the
<literal>MySecondConfiguration</literal> class which in turn has an
<literal>[Import</literal>] attribute pointing to the
<literal>MyThirdConfiguration</literal> class.</para>
<programlisting language="csharp">[Configuration]
[Import(typeof(MySecondConfiguration))]
public class MyConfigurationClass
{
[ObjectDef]
public virtual HomeController HomeController()
{
return new HomeController();
}
}
[Configuration]
[Import(typeof(MyThirdConfiguration))]
public class MySecondConfiguration
{
[ObjectDef]
public virtual AboutController AboutController()
{
return new AboutController();
}
}
[Configuration]
public class MyThirdConfiguration
{
[ObjectDef]
public virtual SomeOtherController SomeOtherController()
{
return new SomeOtherController();
}
}</programlisting>
<para>Given this series of transitive or 'chained'
<literal>[Import]</literal>attributes, the
<literal>CodeConfigApplicationContext</literal> would only need to be
told to scan the single <literal>MyConfigurationClass</literal> type
in order to effectively scan and discover the <literal><link
linkend="objectdef-attribute-reference">[ObjectDef]</link></literal>
methods contained all three <literal><link
linkend="configuration-attribute-reference">[Configuration]</link></literal>
types. Using this approach, its possible to use a compositional
approach to segregate <literal><link
linkend="objectdef-attribute-reference">[ObjectDef]</link></literal>
methods into multiple<literal> <link
linkend="configuration-attribute-reference">[Configuration]</link></literal>
classes and then chain them together just as one might do with XML
based configuration files for many of the other
<literal>IApplicationContext</literal> implementations.</para>
</sect3>
</sect2>
</sect1>
<sect1 xml:id="importresource-attribute-reference">
<title>[ImportResource] Attribute</title>
<para>Just as the <literal>[Import]</literal> attribute provides the
ability to reference and import additional types attributed with the
<literal><link
linkend="configuration-attribute-reference">[Configuration]</link>
</literal>attribute, the <literal>[ImportResrource]</literal> attribute
permits referencing and importing Object Defintions from any
<literal>IResource</literal> implementation including those defined
natively in Spring.NET ("<literal>file://</literal>",
"<literal>assembly://</literal>", etc.).</para>
<sect2>
<title>Using the ImportResourceAttribute</title>
<para></para>
<sect3>
<title>Specifying an IResource</title>
<para>To import an <literal>IResource</literal>, simply reference its
path in the <literal>[ImportResource]</literal> attribute. In the
following example, the embedded assembly resource
<literal>ObjectDefinitions.xml</literal> is being imported into the
process of scanning and parsing the
<literal>MyConfigurationClass</literal> type. This makes all of the
object definitions present in the embedded
<literal>ObjectDefinitions.xml</literal> file available to the
<literal>CodeConfigApplicationContext</literal> as it builds its
Object Defintions.</para>
<programlisting language="csharp">[Configuration]
[ImportResource("assembly://MyApplication.Config/MyCompany.MyApplication.Config/ObjectDefinitions.xml")]
public class MyConfigurationClass
{
[ObjectDef]
public virtual HomeController HomeController()
{
return new HomeController();
}
}</programlisting>
</sect3>
<sect3>
<title>Specifying Multiple IResources</title>
<para>To import multiple <literal>IResource</literal>s, simply provide
multiple <literal>[ImportResource] </literal>attributes, each with the
single resource to import. The following example demonstrates
importing an embedded assembly resource as well as two XML files on
disk.</para>
<programlisting language="csharp">[Configuration]
[ImportResource("assembly://MyApplication.Config/MyCompany.MyApplication.Config/ObjectDefinitions.xml")]
[ImportResource("file://ServiceObjectDefinitions.xml")]
[ImportResource("file://c:/MySpecialConfigLocation/Deployment/SiteB/RepositoryObjectDefinitions.xml")]
public class MyConfigurationClass
{
[ObjectDef]
public virtual HomeController HomeController()
{
return new HomeController();
}
}</programlisting>
</sect3>
<sect3>
<title>Specifying a specific IObjectDefintionReader to Parse the
IResource</title>
<para>By default, the<literal> [ImportResource]</literal> attribute
will use the Spring.NET provided
<literal>XmlObjectDefinitionReader</literal> to parse the imported
<literal>IResource</literal>. If your imported resource cannot be
parsed with the <literal>XmlObjectDefinitionReader</literal> then you
can provide the type of the specific implementation of
<literal>IObjectDefinitionReader</literal> that the<literal>
[ImportResource]</literal> process should use. Note that this provided
type must implement the <literal>IObjectDefinitionReader</literal>
interface or an Exception will be thrown when the
<literal>[ImportResource]</literal> attribute is evaluated.</para>
<programlisting language="csharp">[Configuration]
[ImportResource("assembly://MyApplication.Config/MyCompany.MyApplication.Config/ObjectDefinitions.CSV", DefinitionReader = typeof(MyCommaSeparatedValueObjectDefinitionReader))]
public class MyConfigurationClass
{
[ObjectDef]
public virtual HomeController HomeController()
{
return new HomeController();
}
}</programlisting>
</sect3>
</sect2>
</sect1>
<sect1 xml:id="lazy-attribute-reference">
<title>[Lazy] Attribute</title>
<para>The <literal>[Lazy]</literal> attribute allows you to specify that
the object described by the <literal><link
linkend="objectdef-attribute-reference">[ObjectDef]</link></literal>
should either be be lazily or eagerly instantiated.</para>
<sect2>
<title>Using the Lazy Attribute</title>
<para></para>
<sect3>
<title>Specifying Lazy Instantiation</title>
<para>To specify Lazy Instantiation of any singleton object by the
<literal>CodeConfigApplicationContext</literal>, apply the<literal>
[Lazy] </literal>attribute to any <literal><link
linkend="objectdef-attribute-reference">[ObjectDef]</link></literal>-attributed
method as in the following example. Note that the default usage of
the<literal>[Lazy]</literal> attribute sets Lazy = true and so
<literal>[Lazy]</literal> and <literal>[Lazy(true)]</literal> are
considered functionally equivalent. Also note that specification of
lazy instantiation is only valid for Singleton-scoped objects; any
attempt to apply the <literal>[Lazy]</literal> attribute to a
non-singleton-scoped <literal><link
linkend="objectdef-attribute-reference">[ObjectDef]</link></literal>
method will result in an exception thrown by the underlying
ApplicationContext.</para>
<programlisting language="csharp">[Configuration]
public class MyConfigurationClass
{
[ObjectDef]
[Lazy] //functionally equivalent to [Lazy(true)]
public virtual HomeController HomeController()
{
return new HomeController();
}
[ObjectDef]
[Lazy] //invalid here because the scope is non-Singleton
[Scope(ObjectScope.Prototype)]
public virtual InvalidController InvalidController()
{
return new InvalidController();
}
}</programlisting>
</sect3>
<sect3>
<title>Specifying Non-Lazy (eager) Instantiation</title>
<para>As the default instantiation behavior for the
<literal>CodeConfigApplicationContext</literal> is to perform eager
instantiation, no special attribute needs to be applied to achieve
eager instantiation of the object. However, the
<literal>[Lazy]</literal> attribute will accept a
<literal>bool</literal> value of <literal>false</literal> if you
desire to be explicit about the <literal>[Lazy]</literal> setting for
the <literal><link
linkend="objectdef-attribute-reference">[ObjectDef]</link>
</literal>as shown in the following code snippet.</para>
<programlisting language="csharp">[Configuration]
public class MyConfigurationClass
{
[ObjectDef]
[Lazy(false)] //functionally equivalent to simply not applying the [Lazy] attribute at all
public virtual HomeController HomeController()
{
return new HomeController();
}
}</programlisting>
</sect3>
</sect2>
</sect1>
<sect1 xml:id="scope-attribute-reference">
<title>[Scope] Attribute</title>
<para>The [Scope] attribute permits you to declare the lifetime of the
object managed by the <literal>CodeConfigApplicationContext</literal> for
each <literal><link
linkend="objectdef-attribute-reference">[ObjectDef]</link></literal>-attributed
method.</para>
<sect2>
<title>Scope Attribute Usage</title>
<para>The <literal>[Scope]</literal> attribute accepts a single
<literal>ObjectScope</literal> <literal>enum</literal> argument as
defined by Spring.NET and demonstrated in the following snippet:</para>
<programlisting language="csharp">[Configuration]
public class MyConfigurationClass
{
[ObjectDef]
[Scope(ObjectScope.Prototype)]
public virtual HomeController HomeController()
{
return new HomeController();
}
[ObjectDef]
[Scope(ObjectScope.Singleton)] //technically redundant since all types default to Singleton ObjectScope
public virtual CustomerRepository CustomerRepository()
{
return new CustomerRepository();
}
[ObjectDef]
[Scope(ObjectScope.Session)]
public virtual UserSettings UserSettings()
{
return new UserSettings();
}
}</programlisting>
</sect2>
</sect1>
</chapter>

View File

@@ -0,0 +1,536 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter version="5" xml:id="codeconfig-context"
xmlns="http://docbook.org/ns/docbook"
xmlns:ns5="http://www.w3.org/1998/Math/MathML"
xmlns:ns42="http://www.w3.org/2000/svg"
xmlns:ns4="http://www.w3.org/1999/xlink"
xmlns:ns3="http://www.w3.org/1999/xhtml"
xmlns:ns="http://docbook.org/ns/docbook">
<title>CodeConfigApplicationContext Reference</title>
<para>The <literal>CodeConfigApplicationContext</literal> is an
implementation of <literal>IApplicationContext</literal> designed to gather
its configuration from code-based sources as opposed to XML-based sources as
is the common case with most other <literal>IApplicationContext</literal>
implementations provided by Spring.NET.</para>
<para>This chapter introduces the CodeConfigApplicationContext and how you
can use .NET code to configure the Spring.NET container instead of XML
files. For a general overview on the design of the Spring.NET container
please see <link
ns4:href="http://www.springframework.net/doc-latest/reference/html/objects.html#objects-basics">container
overview</link>. The distribution includes three sample applications, two
console applications (the familiar MovieFinder and a prime number generator)
and a ASP.NET MVC web application. Refer to the <link
linkend="sample-apps">examples section</link> for more information.</para>
<note>
<para>The code-based configuration support requires .NET 2.0 or higher and
solutions that depend upon CodeConfig must be compiled with Visual Studio
2008 or later.</para>
</note>
<sect1>
<title>Concepts</title>
<para>Internally, Spring.NET has a metadata model around the classes it is
responsible for managing, the main abstraction in this metadata model is
the interface <interfacename>IObjectDefinition</interfacename>. The
<interfacename>IObjectDefinition</interfacename> serves as the 'recipie'
that Spring.NET uses to perform dependency injection. In the case of
XML-based configuration sources, XmlApplicationContext parses XML files to
populate the metadata model. In the case of CodeConfig, the
<literal>CodeConfigApplicationContext</literal> scans one or more
assemblies containing one or more types attributed with the <link
linkend="configuration-attribute-reference"><literal>[Configuration]</literal></link>
attribute, parses them to construct appropriate
<literal>ObjectDefinition</literal> instances, and registers those Object
Definitions with the <literal>IApplicationContext</literal> for use. You
can also mix-and-match configuration sources, with some object definitions
originating in XML and others in code.</para>
<sect2>
<title>Using the CodeConfigApplicationContext</title>
<para>The <literal>CodeConfigApplicationContext</literal> usage pattern
consists of the following high-level steps:</para>
<orderedlist>
<listitem>
<para>Instantiate an instance of the
<literal>CodeConfigApplicationContext</literal></para>
</listitem>
<listitem>
<para><emphasis>[optional]</emphasis> Provide one ore more filtering
constraints to control the Assemblies and/or Types to participate in
the scanning</para>
</listitem>
<listitem>
<para>Perform the actual scanning</para>
</listitem>
<listitem>
<para>Initialize (refresh) the
<literal>CodeConfigApplicationContext</literal> which creates the
object definition and eagerly instantiates singleton objects.</para>
</listitem>
</orderedlist>
</sect2>
<sect2>
<title>Mixing and matching configuration metadata formats</title>
<para>You can also use a combination of XML and Code base configuration metadata to configure
the Spring.NET container. If you are an existing user of Spring.NET, incrementally adopting
the code base configuration model will be a common use-case. In this scenario you should use
the component-scan XML namespace to reference configuration metadata. Using the
component-scan namespace requires you to register a custom namespace parser in App.config.
Below is an example showing the use of the code config namespace</para>
<programlisting language="myxml">&lt;objects xmlns="http://www.springframework.net"
xmlns:context="http://www.springframework.net/context"&gt;
&lt;context:component-scan base-assemblies=""/&gt;
&lt;!-- &lt;object/&gt; definitions here --&gt;
&lt;/objects&gt;
</programlisting>
<para>The base-assemblies attribute allows you to express limitations
of Assemblies to scan via a comma seperated list of assembly names.
The filter is a StartsWith filter</para>
<para>You will also need to configure the context namespace parser in
the main .NET application configuraiton file as shown below</para>
<programlisting language="myxml">&lt;configuration&gt;
&lt;configSections&gt;
&lt;sectionGroup name="spring"&gt;
&lt;!-- other Spring config sections handler like context, typeAliases, etc not shown for brevity --&gt;
&lt;section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core"/&gt;
&lt;/sectionGroup&gt;
&lt;/configSections&gt;
&lt;spring&gt;
&lt;parsers&gt;
&lt;parser type="Spring.Context.Config.ContextNamespaceParser, Spring.Core.Configuration" /&gt;
&lt;/parsers&gt;
&lt;/spring&gt;
&lt;/configuration&gt;</programlisting>
<para>You can also start from a CodeConfig class and import XML based
object defintions. The example below shows loading of an embedded XML
resource file using the <literal>[ImportResource]</literal>
attribute.</para>
<programlisting language="csharp">[Configuration]
[ImportResource("assembly://MyApp/MyApp.MyNamespace.MyConfig/ObjectDefinitions.xml"))]
public class DataModuleConfigurationClass
{
[ObjectDef]
public virtual SomeType SomeType()
{
return new SomeType();
}
}</programlisting>
</sect2>
</sect1>
<sect1>
<title>Scanning Basics</title>
<para>The behavior of the <literal>CodeConfigApplicationContext</literal>
scanning operation can be controlled by the following constraints:</para>
<itemizedlist>
<listitem>
<para><emphasis>Assembly Inclusion Constraint</emphasis></para>
<para>Only assemblies matching this contraint will be scanned;
defaults to 'all assemblies'.</para>
</listitem>
</itemizedlist>
<itemizedlist>
<listitem>
<para><emphasis>Type Inclusion Contraint</emphasis></para>
<para>Only types matching this contraint in assemblies matching the
<emphasis>Assembly Inclusion Constraint</emphasis> will be scanned;
defaults to 'all types'.</para>
</listitem>
</itemizedlist>
<sect2>
<title>Scaning all assemblies</title>
<para>The simplest way to get started using code-based configuration is
to instruct the context to scan all assemblies, as shown below</para>
<programlisting language="csharp">var ctx = new CodeConfigApplicationContext();
ctx.ScanAllAssemblies();
ctx.Refresh();</programlisting>
<note>
<para>Despite the name of the method 'ScanAllAssemblies', not all
assemblies need to be scanned for [Configuraiton] attributes. There is
an implicit filter that exclude the .NET BCL libraries as well as the
Spring.NET assemblies since these will never contain any
[Configuration] attributed classes. The name 'ScanAllAssemblies'
should be interpreted as scanning all of the other assemblies you
reference in your application.</para>
</note>
</sect2>
<sect2>
<title>Scanning specific assemblies and types</title>
<para>While the scanning process is very rapid (as compared to the
overhead of parsing XML files) you may want to control the scope of the
scanning operations to match your deployment or other usage models. To
facilitate control of the scope of the scanning operation, the
<literal>CodeConfigApplicationContext</literal> provides several
<literal>.ScanXXX()</literal> methods that accept constraints to be
applied to the assemblies and types during the scanning process. The
format of these constraints is that of
<literal>Predicate&lt;T&gt;</literal> where <literal>T</literal> is
either <literal>System.Reflection.Assembly</literal> or
<literal>System.Type</literal>, respectively. Recall that
<literal>Predicate&lt;T&gt;</literal> is any method that accepts a
single parameter of <literal>Type T</literal> and returns a
<literal>bool</literal>.</para>
<para>The list of scan methods and brief description is shown
below</para>
<table>
<title>Description</title>
<tgroup cols="2">
<tbody>
<row>
<entry><literal>ScanAllAssemblies()</literal></entry>
<entry>Scans all assemblies, except those in the .NET BCL and
Spring distribution</entry>
</row>
<row>
<entry><literal>ScanWithTypeFilter(Predicate&lt;Type&gt;
typePredicate)</literal></entry>
<entry>Scans only those types that match the
<literal>typePredicate</literal></entry>
</row>
<row>
<entry><literal>ScanWithAssemblyFilter(Predicate&lt;Assembly&gt;
assemblyPredicate)</literal></entry>
<entry>Scans only those assemblines that match the
<literal>assemblyPredicate</literal></entry>
</row>
<row>
<entry><literal>Scan(Predicate&lt;Assembly&gt;
assemblyPredicate, Predicate&lt;Type&gt;
typePredicate)</literal></entry>
<entry>Scans the AppDomain root path for assemblies that match
the <literal>assemblyPredicate</literal> and types that match
the <literal>typePredicate</literal></entry>
</row>
<row>
<entry><literal>Scan(AssemblyObjectDefinitionScanner
scanner)</literal></entry>
<entry>Performs the scan using the settings encapsulated in the
provided <literal>ObjectDefintionScanner</literal></entry>
</row>
</tbody>
</tgroup>
</table>
<para>Other sections below provide a more detailed description and usage
examples for the scan methods.</para>
<para>Note that as with any <literal>Predicate&lt;T&gt;</literal>
construct, the <literal>Predicate&lt;Assembly&gt;</literal> and
<literal>Predicate&lt;Type&gt;</literal> constraints may be of arbitrary
complexity, formulated using any combination of standard C# AND
(<literal>&amp;&amp;</literal>), OR (<literal>||</literal>), and NOT
(<literal>!</literal>) operators.</para>
<para>The following example matches assemblies with names containing
"Config" but NOT containing "Configuration" OR containing
"Services":</para>
<programlisting language="csharp">var ctx = new CodeConfigApplicationContext();
ctx.ScanWithAssemblyFilter(assy =&gt; (assy.Name.FullName.Contains("Config") &amp;&amp; !assy.Name.FullName.Contains("Configuration")) || assy.Name.FullName.Contains("Services");</programlisting>
<para>Because its merely a .NET delegate, note that it is also possible
to pass any arbitrary method that satisfies the contract
(<literal>Predicate&lt;T&gt;</literal>), so assuming that the method
<literal>IsOneOfOurConfigAssemblies</literal> is defined elsewhere as
follows...</para>
<programlisting language="csharp">private bool IsOneOfOurConfigAssemblies(Assembly assy)
{
return (assy.Name.FullName.Contains("Config") &amp;&amp; !assy.Name.FullName.Contains("Configuration")) || assy.Name.FullName.Contains("Services");
};</programlisting>
<para>...its possible to express the constraint in the call to
.Scan(...) much more succinctly as follows:</para>
<programlisting language="csharp">var ctx = new CodeConfigApplicationContext();
ctx.ScanWithAssemblyFilter(IsOneOfOurConfigAssemblies);</programlisting>
<para>None of this is anything other than simple .NET Delegate handling,
but its important to take note that the full flexibility of .NET
Delegates and lambda expressions is at your disposal for formulating and
passing scanning constraints.</para>
</sect2>
<sect2>
<title>Assembly Inclusion Constraints</title>
<para>To facilitate limiting the scope of scanning at the Assembly
level, the <literal>CodeConfigApplicationContext</literal> provides
several <literal>.ScanXXX()</literal> method signatures that accept a
constraint to be applied to the assemblies at scan time. The format of
this constraint matches
<literal>Predicate&lt;System.Reflection.Assembly&gt;</literal> (e.g. any
delegate method that accepts a single
<literal>System.Reflection.Assembly</literal> param and returns a
<literal>bool</literal>).</para>
<para>As an example, the following snippet demonstrates the invocation
of the scanning operation such that it will only scan assemblies whose
filename begins with the string
"<literal>MyCompany.MyApplication.Config.</literal>" and so would match
assemblies like
<literal>MyCompany.MyApplication.Config.Services.dll</literal> and
<literal>MyCompany.MyApplication.Config.Infrastructure.dll</literal> but
would not match an assembly named
<literal>MyCompany.MyApplication.Core.dll</literal>.<programlisting
language="csharp">var ctx = new CodeConfigApplicationContext();
ctx.ScanWithAssemblyFilter(assy =&gt; assy.Name.FullName.StartsWith("MyCompany.MyApplication.Config."));</programlisting>Because
the <literal>Predicate&lt;System.Reflection.Assembly&gt;</literal> has
access to the full reflection metadata of each assembly, it is also
possible to indicate assemblies to scan based on properties of one or
more contained types as in the following example that will scan any
assembly that contains at least one <literal>Type</literal> whose name
ends in "<literal>Config</literal>". Note that even though this
constraint is dependent upon <literal>Type</literal> metadata, it is
still a functional <literal>Assembly</literal> contraint, resulting in
filtering only at the Assembly level.</para>
<programlisting language="csharp">var ctx = new CodeConfigApplicationContext();
ctx.ScanWithAssemblyFilter(a =&gt; a.GetTypes().Any(assy =&gt; assy.GetTypes().Any(type =&gt; type.FullName.EndsWith("Config"))));</programlisting>
</sect2>
<sect2>
<title>Type Inclusion Constraints</title>
<para>To limit the scope of scanning of types within assemblies, the
<literal>CodeConfigApplicationContext</literal> provides several
<literal>.ScanXXX()</literal> method signatures that accept a constraint
to be applied to include types within assemblies at scan time. The
format of this contraint matches
<literal>Predicate&lt;System.Type&gt;</literal> (e.g. any delegate
method that accepts a single <literal>System.Type</literal> param and
returns a <literal>bool</literal>).</para>
<para>As an example, the following snippet demonstrates the invocation
of the scanning operation such that it will only scan types whose names
contain the string "Config" and so would match types named
"<literal>MyConfiguration</literal>",
"<literal>ServicesConfiguration</literal>", and
"<literal>ConfigurationSettings</literal>" but not
"<literal>MyClass</literal>".</para>
<programlisting language="csharp">var ctx = new CodeConfigApplicationContext();
ctx.ScanWithTypeFilter(type =&gt; type.FullName.Contains("Config");</programlisting>
<para>There are two important aspects to take note of in re: the
behavior of the <literal>CodeConfigApplicationContext</literal> with
regards to <emphasis>Type Inclusion
Constraints</emphasis><emphasis></emphasis></para>
<itemizedlist>
<listitem>
<para><emphasis>Type Inclusion Constraints</emphasis> are applied
only to types defined in assemblies that also satisfy the
<emphasis>Assembly Inclusion Constraint</emphasis></para>
<para>No matter whether any given <literal>Type</literal> satisfies
the <emphasis>Type Inclusion Contstraint</emphasis>, if the
<literal>Type</literal> is defined in an assembly that fails to
satisfy the <emphasis>Assembly Inclusion Constraint</emphasis>, the
<literal>Type</literal> will not be scanned for Object
Defintions.</para>
</listitem>
<listitem>
<para>There is always an implicit additional <emphasis>Type
Inclusion Constraint</emphasis> of "...and the
<literal>Type</literal> must have the <link
linkend="configuration-attribute-reference"><literal>[Configuration]</literal></link>
attribute applied to it"</para>
<para>No Type that does not have the <link
linkend="configuration-attribute-reference"><literal>[Configuration]</literal></link>
attribute applied to its declaration will ever be scanned regardless
of any <emphasis>Type Inclusion Constraint</emphasis>.</para>
</listitem>
</itemizedlist>
</sect2>
</sect1>
<sect1>
<title>Advanced Scanning Behavior</title>
<para>In some cases, you may want more fine-grained control of the
scanning behavior the <literal>CodeConfigApplicationContext</literal>. In
this section, we explore the various techniques for achieving this level
of control.</para>
<sect2>
<title>Combining Root Path, Assembly Constraints, and Type
Constraints</title>
<para>The <literal>CodeConfigApplicationContext</literal> provides
several <literal>.ScanXXX(...)</literal> method overloads that accept
both an Assembly Constraint and a Type Constraint. These may be combined
as in the following example:</para>
<programlisting language="csharp">var ctx = new CodeConfigApplicationContext();
ctx.Scan(assy =&gt; assy.FullName.Name.BeginsWith("Config"), type =&gt; type.Name.Contains("Infrastructure"));</programlisting>
<para>Note that it is not possible to exclude specific types from the
scanning process using these overloads of the
<literal>.Scan(...)</literal> method. To get type-exclusion control, you
must instantiate and pass in your own instance of the
<literal>AssemblyObjectDefinitionScanner</literal> as described in the
following section(s).</para>
</sect2>
<sect2>
<title>Using your own AssemblyObjectDefintionScanner Instance</title>
<para>If you need more fine-grained control of the scanning behavior of
the <literal>CodeConfigApplicationContext</literal>, you can instantiate
and configure your own instance of the
<literal>AssemblyObjectDefinitionScanner</literal> and pass it to the
<literal>.Scan(...)</literal> method directly. The
<literal>AssemblyObjectDefinitonScanner</literal> provides many methods
for defining the contraints that will control the scanning
process.</para>
<sect3>
<title>Scanning Specific Assemblies and Types</title>
<para>The <literal>AssemblyObjectDefinitionScanner</literal> provides
methods that permit specific assemblies or types to be included or
excluded.</para>
<programlisting language="csharp">var scanner = new AssemblyObjectDefinitionScanner();
scanner.AssemblyHavingType&lt;MyConfigurations&gt;(); //add the assembly containing this type to the list of assemblies to be scanned
scanner.IncludeType&lt;MySpecialConfiguration&gt;(); //add this specific type the list of types to be scanned
scanner.ExcludeType&lt;MyConfigurationToBeIgnored&gt;(); //exclude this specific type from the list of types to be scanned
var ctx = new CodeConfigApplicationContext();
ctx.Scan(scanner);</programlisting>
<para>For those that prefer a more fluent feel to the
<literal>AssemblyObjectDefinitionScanner</literal> API, there are
methods that permit successive filter criteria to be strung together
in a sequence as in the following example:</para>
<programlisting language="csharp">var scanner = new AssemblyObjectDefinitionScanner();
scanner
.WithAssemblyFilter(assy =&gt; assy.FullName.Name.StartsWith("Config"))
.WithIncludeFilter(type =&gt; type.Name.Contains("MyApplication"))
.WithExcludeFilter(type =&gt; type.Name.EndsWith("Service"))
.WithExcludeFilter(type =&gt; type.Name.StartsWith("Microsoft"));
var ctx = new CodeConfigApplicationContext();
ctx.Scan(scanner);</programlisting>
</sect3>
</sect2>
</sect1>
<sect1>
<title>Use of XML Namespace and scanning</title>
<para> By default, classes attributed with  [Component] ,  [Repository] ,  [Service] ,
[Controller] , [Configuration] or a custom attribute that extends [Component]  are the only
detected candidate components. However, you can modify and extend this behavior simply by
applying custom filters. Add them as  <emphasis role="italic">include-filter</emphasis>  or 
<emphasis role="italic">exclude-filter</emphasis>  sub-elements of the  component-scan
element. Each filter element requires the  type  and  expression  attributes. The following
table describes the filtering options. </para>
<para>
<table frame="all">
<title>Table Filter Types</title>
<tgroup cols="3">
<colspec colname="c1" colnum="1" colwidth="1*"/>
<colspec colname="c2" colnum="2" colwidth="4.24*"/>
<colspec colname="c3" colnum="3" colwidth="3.85*"/>
<thead>
<row>
<entry>Filter Type</entry>
<entry>Example Expression</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>attribute</entry>
<entry><literal>Spring.Stereotype.RepositoryAttribute, Spring.Core</literal></entry>
<entry> An attribute to be present at the type level in target components. </entry>
</row>
<row>
<entry>assignable</entry>
<entry><literal>My.Namespace.IFoo, My.Assembly</literal></entry>
<entry> A class (or interface) that the target components are assignable to
(extend/implement). </entry>
</row>
<row>
<entry>regex</entry>
<entry><literal>My.NameSpace.*.*Dao</literal></entry>
<entry> A regex expression to be matched by the target components class names.
</entry>
</row>
<row>
<entry>custom</entry>
<entry><literal>My.Namespace.MyTypeFilter, My.Assembly</literal></entry>
<entry> A custom implementation of the
<literal>Spring.Context.Attributes.TypeFilters.ITypeFilter</literal> interface. </entry>
</row>
</tbody>
</tgroup>
</table>
</para>
<para> The following example shows the XML configuration ignoring all [Repository]  attributed
classes and only scanning types with names that end with Dao using "stub" repositories
instead. </para>
<programlisting>&lt;objects>
&lt;context:component-scan base-assemblies="My.Namespace">
&lt;context:include-filter type="regex" expression=".*Dao"/>
&lt;context:exclude-filter type="annotation" expression="Spring.Stereotype.RepositoryAttribute, Spring.Core"/>
&lt;/context:component-scan>
&lt;/beans> </programlisting>
</sect1>
</chapter>

View File

@@ -0,0 +1,700 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter version="5" xml:id="codeconfig-migration-example"
xmlns="http://docbook.org/ns/docbook"
xmlns:ns5="http://www.w3.org/1998/Math/MathML"
xmlns:ns42="http://www.w3.org/2000/svg"
xmlns:ns4="http://www.w3.org/1999/xlink"
xmlns:ns3="http://www.w3.org/1999/xhtml"
xmlns:ns="http://docbook.org/ns/docbook">
<title>Introducing CodeConfig</title>
<sect1>
<title>A Dependency Injection Example</title>
<para>We will introduce the new code based approach by working with a very
simple application that will provide us the context to understand the
concepts of CodeConfig. We start by examining a sample application that
uses Spring.NET configured via traditional XML configuration files. Then
we show how CodeConfig can be used to achieve the same results without any
XML configuration files at all.</para>
<para>To begin with, lets explore the sample application that we will be
working with. This sample app is included in the Spring.NET CodeConfig
download package in the
<literal>/examples/Spring.CodeConfig.Migration</literal> folder.</para>
<para>To keep things simple, its just a .NET console application designed
to calculate and display the prime numbers between zero and an arbitrary
maximum number. There are four classes that must collaborate together to
do the work: <literal>ConsoleReporter</literal>,
<literal>PrimeGenerator</literal>,
<literal>PrimeEvaluationEngine</literal>, and
<literal>OutputFormatter</literal>. <literal>ConsoleReporter</literal>
depends on the <literal>PrimeGenerator</literal> which in turn depends on
the <literal>PrimeEvaluationEngine</literal> to calculate the prime
numbers. <literal>ConsoleReporter</literal> also depends on
<literal>OutputFormatter</literal> to format the results. The main console
application then simply asks <literal>ConsoleReporter</literal> to write
its report and <literal>ConsoleReporter</literal> goes to work. The
following Figure is a UML class diagram showing a simple way to visualize
the dependencies between these objects.</para>
<para><screenshot>
<mediaobject>
<imageobject>
<imagedata fileref="images/Migration_App_UML_Diagram.png">
<info>
<author>
<personname></personname>
</author>
<pubdate></pubdate>
</info>
</imagedata>
</imageobject>
</mediaobject>
</screenshot></para>
<para>A simple <literal>Main()</literal> method that would do this without
the Spring.NET container could look something like Listing 1. Note the
in-line injection of dependencies via constructor arguments that builds up
the collaborating objects.</para>
<programlisting language="csharp" linenumbering="unnumbered">//Listing 1 (sample Main method not using Spring.NET)
static void Main(string[] args)
{
ConsoleReport report = new ConsoleReport(
new OutputFormatter(),
new PrimeGenerator(new PrimeEvaluationEngine()));
report.MaxNumber = 1000;
report.Write();
Console.WriteLine("--- hit enter to exit --");
Console.ReadLine();
}</programlisting>
<para>Using Spring.NET, as opposed to manually injecting dependencies as
in Listing 1, the collaborating objects are composed together with their
dependencies injected by the Spring.NET container at run-time. Initially,
the configuration of these objects is controlled from a Spring.NET XML
configuration file (see Listing 2).</para>
<programlisting language="xml">&lt;!-- Listing 2 (Spring.NET XML Configuration file, application-context.xml) --&gt;
&lt;?xml version="1.0" encoding="utf-8" ?&gt;
&lt;objects xmlns="http://www.springframework.net"&gt;
&lt;object name="ConsoleReport" type="Primes.ConsoleReport, Primes"&gt;
&lt;constructor-arg ref="PrimeGenerator"/&gt;
&lt;constructor-arg ref="OutputFormatter"/&gt;
&lt;property name="MaxNumber" value="1000"/&gt;
&lt;/object&gt;
&lt;object name="PrimeGenerator" type="Primes.PrimeGenerator, Primes"&gt;
&lt;constructor-arg&gt;
&lt;object type="Primes.PrimeEvaluationEngine, Primes"/&gt;
&lt;/constructor-arg&gt;
&lt;/object&gt;
&lt;object name="OutputFormatter" type="Primes.OutputFormatter, Primes"/&gt;
&lt;/objects&gt;</programlisting>
<para>In Listing 2 you can also see the use of “<literal>ref</literal>
element to refer to collaborating objects and the property
<literal>MaxNumber</literal>” being set to “<literal>1000</literal>” on
the <literal>ConsoleReport</literal> object after its constructed. This
is the maximum number up to which we want the software to calculate prime
numbers. In Listing 3 we see the construction of the
<literal>XmlApplicationContext</literal> which is initialized by passing
it the name of the XML Configuration file. This container is then used to
resolve the <literal>ConsoleReport</literal> object with all of its
dependencies properly satisfied and its <literal>MaxNumber</literal>
property assigned the value of <literal>1000</literal>.</para>
<programlisting language="csharp">//Listing 3 (initializing the XmlApplicationContext container)
static void Main(string[] args)
{
IApplicationContext ctx = CreateContainerUsingXML();
ConsoleReport report = ctx["ConsoleReport"] as ConsoleReport;
report.Write();
ctx.Dispose();
Console.WriteLine("--- hit enter to exit --");
Console.ReadLine();
}
private static IApplicationContext CreateContainerUsingXML()
{
return new XmlApplicationContext("application-context.xml");
}</programlisting>
<para>While this XML-based configuration is well-understood by Spring.NET
users and others alike as a common method for expressing configuration
settings, it suffers from several challenges common to all XML file
including being overly-verbose and full of string-literals that are
unfriendly to most of the modern refactoring tools.</para>
</sect1>
<sect1>
<title>Migration to CodeConfig</title>
<para>To reduce or even eliminate the use of XML for configuring the
Spring.NET DI container, lets look at how we can express the same
configuration metadata in code using Spring.NET CodeConfig. There are
several steps to using CodeConfig. We will look at each of them in the
likely sequence that one would follow to convert an existing XML-based
configuration for Spring.NET over to use the CodeConfig approach.</para>
<sect2>
<title>The CodeConfig Classes</title>
<sect3>
<title>Creating the CodeConfig Classes</title>
<para>First, we need to construct one or more classes to contain our
configuration metadata and attribute them properly. Spring.NET
CodeConfig relies upon attributes applied to classes and methods to
convey its metadata. Shown in Listing 4 is the CodeConfig class
(<literal>PrimesConfiguration</literal>) for our sample application.
<programlisting language="csharp">// Listing 4, (Spring.NET Configuration Class, PrimesConfiguration.cs)
using System;
using System.Configuration;
using Primes;
using Spring.Context.Attributes;
namespace SpringApp
{
[Configuration]
public class PrimesConfiguration
{
[ObjectDef]
public virtual ConsoleReport ConsoleReport()
{
ConsoleReport report = new ConsoleReport(OutputFormatter(), PrimeGenerator());
report.MaxNumber = Convert.ToInt32(ConfigurationManager.AppSettings.Get("MaximumNumber"));
return report;
}
[ObjectDef]
public virtual IOutputFormatter OutputFormatter()
{
return new OutputFormatter();
}
[ObjectDef]
public virtual IPrimeGenerator PrimeGenerator()
{
return new PrimeGenerator(new PrimeEvaluationEngine());
}
}
}</programlisting></para>
</sect3>
<sect3>
<title>Elements of the CodeConfig Classes</title>
<para>Lets explore the important elements of the CodeConfig file in
Listing 4 to understand how it can convey the same information to the
Spring.NET container as the XML file in Listing 2.</para>
<sect4>
<title>The Class</title>
<para>At the class level, you will notice the
<literal>PrimesConfiguration</literal> class has the<literal><link
linkend="configuration-attribute-reference">[Configuration]</link></literal>
attribute applied to it. During the initialization phase of the DI
container, Spring.NET CodeConfig reads classes with these
attributes. Note that there is no specific inheritance hierarchy
required of a configuration class: no special base class or
interface implementation is required, leaving you free to leverage
inheritance and polymorphism to achieve some interesting
configuration and composition scenarios. Also note these special
identifying attributes are only applied to your CodeConfig classes,
not the types for which they are providing object definition
metadata. This means that your classes that do the work of your
application (e.g., <literal>ConsoleReport</literal>,
<literal>PrimeGenerator</literal>, etc.) are free to remain
undiluted POCO (Plain-Old-CLR-Object) classes that have themselves
no direct dependency on the Spring.NET framework.</para>
</sect4>
<sect4>
<title>The Methods</title>
<para>At the member level of the
<literal>PrimesConfiguration</literal> class, you will notice
several methods having the <literal><link
linkend="objectdef-attribute-reference">[ObjectDef]</link></literal>
attribute. This attribute identifies the method to which it is
applied as being the logical representation of a single object
definition for the Spring.NET container.</para>
<para>To begin understanding how this works lets look at the
simplest of the definitions, that of the OutputFormatter. Lets
start with the method visibility: all <link
linkend="objectdef-attribute-reference"><literal>[ObjectDef]</literal></link>
methods must be declared both public and virtual.</para>
<sidebar>
<title>Why must the [ObjectDef] methods be virtual?</title>
<para>The requirement for the <link
linkend="objectdef-attribute-reference"><literal>[ObjectDef]</literal></link>
methods being virtual comes from the need when using CodeConfig
for the container to proxy <link
linkend="objectdef-attribute-reference"><literal>[ObjectDef]</literal></link>
methods on the <link
linkend="configuration-attribute-reference"><literal>[Configuration]</literal></link>
classes. When the container is asked for an object, this proxy
intercepts the invocation of the <link
linkend="objectdef-attribute-reference"><literal>[ObjectDef]</literal></link>
methods and ensures that requests for objects respect object
scoping rules like singleton and prototype. Singleton scope
ensures that if you ask the container multiple times for the same
named object, it will always return the same instance rather than
a new one each time. Singleton scope is common in server-side
programming and is the default lifecycle in Spring.NET.</para>
</sidebar>
<para>The method return type, <literal>IOutputFormatter</literal>,
becomes the type that the DI container will be configured to
register. The method name itself,
<literal>OutputFormatter</literal>, is the equivalent of the id or
name that will be assigned to the object in the container. This name
can also be controlled by setting the <literal>Names</literal>
property on the <link
linkend="objectdef-attribute-reference"><literal>[ObjectDef]</literal></link>
attribute itself.</para>
<para>The body of the <literal>OutputFormatter()</literal> method
simply creates a new instance of the
<literal>OutputFormatter</literal> and returns it. In simple terms,
we can think of the<literal> OutputFormatter()</literal> method as a
factory method that knows how to construct and return an instance of
something that implements the <literal>IOutputFormatter</literal>
interface (in this case, the concrete
<literal>OutputFormatter</literal> class).</para>
</sect4>
<sect4>
<title>More Complex Methods</title>
<para>To understand a slightly more complex <link
linkend="objectdef-attribute-reference"><literal>[ObjectDef]</literal></link>
method, lets now examine the <literal>PrimeGenerator()</literal>
method. Given what we already know about CodeConfig, its easy to
see that the <literal>PrimeGenerator()</literal> method describes an
Object Definition that will be registered with the container under
the name “<literal>PrimeGenerator</literal>” (the method name) and
the type <literal>IPrimeGenerator</literal> (the return type of the
method).</para>
<para>The method needs to return a new
<literal>PrimeGenerator</literal> but unlike the
<literal>OutputFormater</literal> class that offers an empty default
constructor, the <literal>PrimeGenerator</literal> class only
public constructor requires an instance of the
<literal>PrimeEvaluationEngine</literal> class be passed to it. To
satisfy this constructor dependency, we simply create a new
<literal>PrimeEvaluationEngine</literal> object in-line and pass it
to the new <literal>PrimeGenerator</literal> class. In this way, the
dependency between <literal>PrimeGenerator</literal> and
<literal>PrimeEvaluationEngine</literal> is satisfied in much the
same way as when coded by hand as shown in Listing 1.</para>
<para>As a slightly more complex <link
linkend="objectdef-attribute-reference"><literal>[ObjectDef]</literal></link>
example, lets examine the <literal>ConsoleReport()</literal> method
next. This method needs to return a new
<literal>ConsoleReport</literal> instance, but as with the
<literal>PrimeGenerator</literal> class we lack a zero-argument
public constructor. The only public constructor of the
<literal>ConsoleReport</literal> class requires an
<literal>IOutputFormatter</literal> instance and an
<literal>IPrimeGenerator</literal> instance be provided. In our call
to new up an instance of the <literal>ConsoleReport</literal> class
in the <literal>ConsoleReport()</literal> <link
linkend="objectdef-attribute-reference"><literal>[ObjectDef]</literal></link>
method, we are invoking the other <link
linkend="objectdef-attribute-reference"><literal>[ObjectDef]</literal></link>
methods themselves to return these types. Since these other methods
in turn return <literal>IOutputFormatter</literal> and
<literal>IPrimeGenerator</literal> instances respectively, calls to
these other methods will satisfy the constructor dependency of the
<literal>ConsoleReport</literal> class and thus permit us to create
a new <literal>ConsoleReport</literal> to return at the end of the
<literal>ConsoleReport()</literal> method itself. In this manner, we
are delegating from one <literal>[ObjectDef] </literal>method to
the other <link
linkend="objectdef-attribute-reference"><literal>[ObjectDef]</literal></link>
methods to create the object graph that we seek to return from the
call to the <literal>ConsoleReport()</literal> method.</para>
</sect4>
<sect4>
<title>Controlling Properties on Objects</title>
<para>But what about the “<literal>MaxNumber</literal>” property
that is set for the <literal>ConsoleReport</literal> object in the
XML file in Listing 2? As you can see from Listing 4, setting this
property on our <literal>ConsoleReport</literal> object is as simple
as…well, setting the property on our
<literal>ConsoleReport</literal> object! Since our
<literal>ConsoleReport()</literal> method merely has to return a new
<literal>ConsoleReport</literal> instance, we are completely free to
use any approach (in code) we choose to modify the
<literal>ConsoleReport</literal> instance before we return it. In
this case, its a simple matter of reading the value out of the
<literal>App.Config</literal> file and then setting the property to
the desired value before we return the instance of the
<literal>ConsoleReport</literal> object from the method.</para>
</sect4>
</sect3>
</sect2>
<sect2>
<title>Creating and Initializing the Application Context</title>
<para>Once we have translated the XML configuration file in Listing 2
into the CodeConfig class in Listing 4, we need to tell our application
to use it. For that, we need to switch from encapsulating our container
in the Spring.NET <literal>XmlApplicationContext</literal> to
encapsulating it in the <literal>CodeConfigApplicationContext</literal>
instead. Just as the <literal>XmlApplicationContext</literal> is
designed to use XML as the initial entry point to its configuration
settings, the <literal>CodeConfigApplicationContext</literal> is
designed to scan assemblies for <link
linkend="configuration-attribute-reference"><literal>[Configuration]</literal></link>
classes and <link
linkend="objectdef-attribute-reference"><literal>[ObjectDef]</literal></link>
methods as the initial entry point to its configuration settings.
Listing 5 shows the<literal> CreateContainerUsingCodeConfig()</literal>
method from the <literal>Program.cs</literal> file in the sample
application that demonstrates this process.</para>
<programlisting language="csharp">//Listing 5 (bootstrapping the CodeConfigApplicationContext from Program.cs)
private static IApplicationContext CreateContainerUsingCodeConfig()
{
CodeConfigApplicationContext ctx = new CodeConfigApplicationContext();
ctx.ScanAllAssemblies();
ctx.Refresh();
return ctx;
}</programlisting>
<para>After instantiating the
<literal>CodeConfigApplicationContext</literal>, we next invoke the
<literal>ScanAllAssemblies() </literal>method to perform the scanning
for the <link
linkend="configuration-attribute-reference"><literal>[Configuration]</literal></link>-attributed
classes and <link
linkend="objectdef-attribute-reference"><literal>[ObjectDef]</literal></link>-attributed
methods within our project. Lastly, the container is initialized by
calling the <literal>Refresh()</literal> method and then the
ready-to-use context is returned from the method. In the invocation of
the <literal>ScanAllAssemblies()</literal> method, we are asking the
<literal>CodeConfigApplicationContext</literal> to scan the current
AppDomains root folder and all subfolders recursively for all
assemblies that might contain CodeConfig classes (classes having the
<link
linkend="configuration-attribute-reference"><literal>[Configuration]</literal></link>
attribute).</para>
</sect2>
</sect1>
<sect1>
<title>More Granular Control Using CodeConfig</title>
<para>The example in Listing 4 and Listing 5 demonstrates only the most
basic use-cases for CodeConfig. More granular control over each of the
definitions is provided by applying additional attributes to the <link
linkend="configuration-attribute-reference"><literal>[Configuration]</literal></link>
classes and <link
linkend="objectdef-attribute-reference"><literal>[ObjectDef]</literal></link>
methods and by setting various values on these attributes. Among these are
the following:</para>
<itemizedlist>
<listitem>
<para><literal><link
linkend="scope-attribute-reference">[Scope]</link></literal> for
controlling object lifecycle settings on a <link
linkend="objectdef-attribute-reference"><literal>[ObjectDef]</literal></link>
such as singleton, prototype, etc.</para>
</listitem>
<listitem>
<para><literal><link
linkend="import-attribute-reference">[Import]</link> </literal>for
chaining <link
linkend="configuration-attribute-reference"><literal>[Configuration]</literal></link>
classes together so that you can logically divide your <link
linkend="objectdef-attribute-reference"><literal>[ObjectDef]</literal></link>
methods among multiple classes in much the same way you may do so with
multiple XML files that provide pointers to other XML files</para>
</listitem>
<listitem>
<para><literal><link
linkend="importresource-attribute-reference">[ImportResource]</link></literal>
for combining <link
linkend="configuration-attribute-reference"><literal>[Configuration]</literal></link>
classes with any of Spring.NETs many implementations of its
<literal>IResource</literal> abstraction (file://, assembly://, etc.),
the most common one being an XML resource so that you can define part
of your configuration metadata in <link
linkend="configuration-attribute-reference"><literal>[Configuration]</literal></link>
classes and other parts of it in XML files as either embedded
resource(s) in your assemblies or as file(s) on disk.</para>
</listitem>
<listitem>
<para><literal><link
linkend="lazy-attribute-reference">[Lazy]</link></literal> for
controlling lazy instantiation of singleton objects</para>
</listitem>
<listitem>
<para>If you require aliases (additional, multiple names) for the Type
in the container, the <literal><link
linkend="objectdef-attribute-reference">[ObjectDef]</link></literal>
attribute also accepts an array of strings that if provided will be
registered as aliases for the Type registration.</para>
</listitem>
</itemizedlist>
<para>In addition, finer-grained control of the specific assemblies to
scan, and specific <link
linkend="configuration-attribute-reference"><literal>[Configuration]</literal></link>
classes to include and/or exclude is supported by the scanning API. It is
also possible to compose configurations by dividing your <link
linkend="objectdef-attribute-reference"><literal>[ObjectDef]</literal></link>
methods into multiple different <link
linkend="configuration-attribute-reference"><literal>[Configuration]</literal></link>
classes and then to assemble them as building blocks to configure your
container as it is initialized.</para>
<para>The CodeConfig approach enables us to express the same configuration
metadata to our Dependency Injection container as the XML file in Listing
2 had provided, but in a form that is at once both significantly more
powerful and flexible as well as more resilient to the refactoring our
container-managed application objects.</para>
<para>To address additional common non-XML configuration scenarios, such
as the XML schemas for AOP and Transaction management, Spring.NET is also
evolving a more fluent-style configuration API that will build upon
CodeConfig in even more flexible ways in the near future including
convention-based registration of objects.</para>
</sect1>
<sect1>
<title>Organizing and Composing Multiple [Configuration] Classes</title>
<para>The <link linkend="sample-apps">examples</link> referenced in this
document and provided in this distribution almost all employ merely a
single <link
linkend="configuration-attribute-reference">[Configuration]</link> class
from which their ApplicationContext is to be configured. For these simple
examples this approach is viable but just as is the case when configuring
the ApplicationContext via XML, any significantly complex solution is
likely to require separating your <link
linkend="objectdef-attribute-reference">[ObjectDef]</link> methods into
multiple <link
linkend="configuration-attribute-reference">[Configuration]</link>
classes.</para>
<para>Just as there are several strategies for effectively managing
multiple XML configuration files, so too are there many options available
to the developer to organize and compose multiple <link
linkend="configuration-attribute-reference">[Configuration]</link> classes
together in a larger solution. This section doesn't attempt to cover all
of the available options in deep detail, but is intended to provide a
high-level understanding of some of the techniques that can be combined
together to help manage multiple such classes. Users familair with the
common techniques for composing together multiple XML configuration files
for Spring.NET will recognize some of these same patterns applied to <link
linkend="configuration-attribute-reference">[Configuration]</link> classes
in the following sections as well.</para>
<sect2>
<title>Multiple Stand-Alone Configuration Classes</title>
<para>The simplest organization approach is providing multiple
stand-alone <link
linkend="configuration-attribute-reference">[Configuration]</link>
classes. In this scenario, <link
linkend="objectdef-attribute-reference">[ObjectDef]</link> methods are
organized into separate <link
linkend="configuration-attribute-reference">[Configuration]</link>
classes but each of the <link
linkend="configuration-attribute-reference">[Configuration]</link>
classes is entirely self-contained and unrealted to the others. The
decomposition principles can of course be anything of your choosing. One
simple possibility might be to divide your configuration data between
different kinds of services as in the following example:</para>
<programlisting language="csharp">[Configuration]
public class WcfServicesConfigurations
{
[ObjectDef]
public virtual IWebService MySpecialService()
{
//construct and return a IWebService implementation here
}
}
[Configuration]
public class RepositoryServicesConfigurations
{
[ObjectDef]
public virtual ICustomerRepository MyCustomerRepository()
{
//construct and return a ICustomerRepository implementation here
}
[ObjectDef]
public virtual IShippingRepository MyShippingRepository()
{
//construct and return a IShippingRepository implementation here
}
}</programlisting>
<para>In this example, both of these <literal><link
linkend="configuration-attribute-reference">[Configuration]</link></literal>
classes would need to be explicitly scanned and registered with the
<literal><link
linkend="codeconfig-context">CodeConfigApplicationContext</link></literal>
since they each are completely stand-alone and both are needed for the
proper configuration of the ApplicationContext. Since these two classes
in this example have no interdependencies between them, each class may
be placed into a different file or even assembly.</para>
</sect2>
<sect2>
<title>High-Level [Configuration] Classes that [Import] Others</title>
<para>Another strategy for composing multiple <link
linkend="configuration-attribute-reference"><literal>[Configuration]</literal></link>
classes together is to devise one or more 'high-level entry-point'
classes and leverage the <literal><link
linkend="import-attribute-reference">[Import]</link></literal> attribute
so that the scanning of the high-level class automatically imports one
or more lower-level classes. The high-level classes may contain
[ObjectDef] methods of their own or merely hold reference to one or
more [Import] classes as needed.</para>
<programlisting language="csharp">[Configuration]
[Import(typeof(MyWebServicesConfigurations))]
[Import(typeof(MyMessagingServicesConfigurations))]
[Import(typeof(MyPersistenceServicesConfigurations))]
public class ServicesConfigurations
{
//rest of class here as needed
}</programlisting>
<para>In this example, only the
<literal>ServicesConfigurations</literal> class needs to be scanned
because the <link
linkend="import-attribute-reference"><literal>[Import]</literal></link>
attributes point directly to the other classes to scan for
<literal><link
linkend="configuration-attribute-reference">[Configuration]</link></literal>
and <literal linkend="objectdef-attribute-reference"><link
linkend="objectdef-attribute-reference">[ObjectDef]</link></literal>
metadata.</para>
</sect2>
<sect2>
<title>Referencing [ObjectDef]s from one [Configuration] Class in
Another</title>
<para>Once you decompose your <link
linkend="objectdef-attribute-reference"><literal>[ObjectDef]</literal></link>
methods into separate classes, often you will find that you have a need
to reference the objects defined in one <link
linkend="configuration-attribute-reference"><literal>[Configuration]</literal></link>
class when coding the <link
linkend="objectdef-attribute-reference"><literal>[ObjectDef]</literal></link>
methods in another <link
linkend="configuration-attribute-reference"><literal>[Configuration]</literal></link>
class. The architecture of Spring CodeConfig for .NET makes it simple to
address this need: you can simply ask the ApplicationContext to resolve
the needed Type.</para>
<para>To understand how this works, its first important to understand
that <literal><link
linkend="configuration-attribute-reference">[Configuration]</link></literal>
classes are themselves registered as types in the ApplicationContext
<emphasis>in addition to</emphasis> the types defined in their <link
linkend="objectdef-attribute-reference"><literal>[ObjectDef]</literal></link>
methods. Combining that knowledge with the special
<literal>IApplicationContextAware</literal> interface in Spring.NET
allows us to ask the ApplicationContext to inject
<emphasis>itself</emphasis> into our <literal><link
linkend="configuration-attribute-reference">[Configuration]</link></literal>
classes. This ApplicationContext is then available to us to resolve
requests for needed types that may be defined elsewhere, whether in
other <link
linkend="configuration-attribute-reference"><literal>[Configuration]</literal></link>
classes or perhaps even other XML files.</para>
<para>Let's explore the following example where the
<literal>SecondConfiguration</literal> class needs access to the
<literal>TransactionManager</literal> that is defined in the
<literal>FirstConfiguration</literal> class in order to properly build
and configure a <literal>CustomerRepository</literal> instance:</para>
<programlisting language="csharp">[Configuration]
public class FirstConfiguration
{
[ObjectDef]
public virtual TransactionManager MySpecialTransactionManager()
{
return new TransactionManager();
}
}
[Configuration]
public class SecondConfiguration : IApplicationContextAware //note the interface implementation
{
//field to hold the injected context
private IApplicationContext _context;
//property setter defined by the IApplcationContextAware interface
// so that the context can inject itself into the class
public IApplicationContext ApplicationContext { set { _context = value; } }
[ObjectDef]
public virtual ICustomerRepository CustomerRepository()
{
//to construct the CustomerRepository, we need a TransactionManager instance
// as a constructor argument so let's ask the injected context to resolve one for us
return new CustomerRepository(_context.GetObject&lt;TransactionManager&gt;());
}
}
//somewhere else in your solution the CustomerRepository class is defined as follows...
public class CustomerRespository : ICustomerRespository
{
private TransactionManager _transactionManager;
public CustomerRespository(TransactionManager transactionManager)
{
_transactionManager = transactionManager;
}
}</programlisting>
<para>In this way, there is no direct coupling between <literal><link
linkend="configuration-attribute-reference">[Configuration]</link></literal>
classes and the SecondConfiguration class is only aware of the
ApplicationContext itself and the actual Types it needs to construct the
Types described in its <link
linkend="objectdef-attribute-reference"><literal>[ObjectDef]</literal></link>
methods.</para>
</sect2>
</sect1>
</chapter>

View File

@@ -0,0 +1,266 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter version="5" xml:id="codeconfig-sample-apps"
xmlns="http://docbook.org/ns/docbook"
xmlns:ns52="http://www.w3.org/1999/xhtml"
xmlns:ns5="http://www.w3.org/1999/xlink"
xmlns:ns4="http://www.w3.org/2000/svg"
xmlns:ns3="http://www.w3.org/1998/Math/MathML"
xmlns:ns="http://docbook.org/ns/docbook">
<title>Sample Applications</title>
<section>
<title>Overview</title>
<para>The Spring CodeConfig for .NET project includes three sample
applications:</para>
<itemizedlist>
<listitem>
<para><link
linkend="sample-moviefinder-codeconfig">Spring.IoCQuickStart.MovieFinder</link></para>
</listitem>
<listitem>
<para><link
linkend="sample-mvc-codeconfig">Spring.MvcQuickStart</link></para>
</listitem>
<listitem>
<para><link
linkend="sample-migration-codeconfig">Spring.CodeConfig.Migration</link></para>
</listitem>
</itemizedlist>
</section>
<section xml:id="sample-moviefinder-codeconfig">
<title>Spring.IoCQuickStart.MovieFinder</title>
<para>The Spring.IoCQuickStart.MovieFinder sample application is the same
fundamental code as provided in the sample app by the same name that is
provided with <link
ns5:href="http://springframework.net/download.html">the main Spring.NET
Framework download package</link>. This section assumes you are already
familiar with the sample application as provided in that download package.
This version of this sample application has been modified to rely upon
CodeConfig for its Object Definitions.</para>
<section>
<title>Modifying App.config</title>
<para>The first modification to the existing sample application involves
changes to the <literal>&lt;spring&gt;</literal> element in the
App.config file:</para>
<programlisting language="xml"> &lt;spring&gt;
&lt;parsers&gt;
&lt;!-- Equivalent to 'NamespaceParserRegistry.RegisterParser(typeof(ContextNamespaceParser));' --&gt;
&lt;parser type="Spring.Context.Config.ContextNamespaceParser, Spring.Core.Configuration" /&gt;
&lt;/parsers&gt;
&lt;context&gt;
&lt;!-- using embedded assembly configuration file --&gt;
&lt;resource uri="assembly://Spring.IocQuickStart.MovieFinder/Spring.IocQuickStart.MovieFinder/CodeConfigBootstrap.xml"/&gt;
&lt;/context&gt;
&lt;/spring&gt;</programlisting>
<para>The important changes here are the introducton of the<literal>
&lt;parsers&gt;</literal> section (to register the
<literal>ContextNamespaceParser</literal> required for CodeConfig) and
the change of the <literal>&lt;resource&gt;</literal> element to point
to the <literal>CodeConfigBootstrap.xml</literal> file instead of the
<literal>Appcontext.xml</literal> file as before.</para>
</section>
<section>
<title>Replacing Appcontext.xml with CodeConfigBootstrap.xml</title>
<para>Where before the <literal>Appcontext.xml</literal> contained all
of the XML-based object definitions for the application, in this case
the <literal>CodeConfigBootstrap.xml</literal> merely contains the
single instruction telling the context to use CodeConfig:</para>
<programlisting language="xml">&lt;?xml version="1.0" encoding="utf-8" ?&gt;
&lt;objects xmlns="http://www.springframework.net"
xmlns:context="http://www.springframework.net/context"&gt;
&lt;description&gt;An example that demonstrates code based configuration for object definitions.&lt;/description&gt;
&lt;context:code-config/&gt;
&lt;/objects&gt;
</programlisting>
<para>In this case, the single
<literal>&lt;context:code-config/&gt;</literal> element is sufficient to
tell the context to perform scanning for its object definition
metadata.</para>
</section>
<section>
<title>Introducing the MovieFinderConfiguration CodeConfig class</title>
<para>The final modification needed to migrate the sample application to
rely upon CodeConfig is to author the classes that will provide the
Object Definitions for the Application Context to discover when
performing the scan. In the case of this simple application, this is
encapsulated in the single new
<literal>MovieFinderConfiguration</literal> class as shown in the
following:</para>
<programlisting language="csharp"> [Configuration]
public class MovieFinderConfiguration
{
[ObjectDef]
public virtual MovieLister MyMovieLister()
{
MovieLister movieLister = new MovieLister();
movieLister.MovieFinder = FileBasedMovieFinder();
return movieLister;
}
[ObjectDef]
public virtual IMovieFinder FileBasedMovieFinder()
{
return new ColonDelimitedMovieFinder(new FileInfo("movies.txt"));
}
}</programlisting>
<para>Using a combination of <literal><link
linkend="configuration-attribute-reference">[Configuration]</link></literal>
and <link
linkend="objectdef-attribute-reference"><literal>[ObjectDef]</literal></link>
attributes, this class faithfully reproduces the same Object Definition
configuration as had been described by the
<literal>Appcontext.xml</literal> file in the iniitial sample
application as delivered with the main Spring.NET Framework
download.</para>
</section>
</section>
<section xml:id="sample-mvc-codeconfig">
<title>Spring.MvcQuickStart</title>
<para>The Spring.MvcQuickStart sample application is the same fundamental
code as provided in the sample app by the same name that is provided with
<link ns5:href="http://springframework.net/download.html">the main
Spring.NET Framework download package</link>. This section assumes you are
already familiar with the sample application as provided in that download
package. This version of this sample application has been modified to rely
upon CodeConfig for its Object Definitions.</para>
<section>
<title>Modifying Web.config</title>
<para>The first modification to the existing sample application involves
changes to the <literal>&lt;spring&gt;</literal> element in the
Web.config file:</para>
<programlisting language="xml">&lt;spring&gt;
&lt;parsers&gt;
&lt;!-- Equivalent to 'NamespaceParserRegistry.RegisterParser(typeof(ContextNamespaceParser));' --&gt;
&lt;parser type="Spring.Context.Config.ContextNamespaceParser, Spring.Core.Configuration" /&gt;
&lt;/parsers&gt;
&lt;context&gt;
&lt;resource uri="~/Config/CodeConfigBootsrap.xml"/&gt;
&lt;/context&gt;
&lt;/spring&gt;</programlisting>
<para>The important changes here are the introducton of
the<literal>&lt;parsers&gt;</literal> section (to register the
<literal>ContextNamespaceParser</literal> required for CodeConfig) and
the change of the <literal>&lt;resource&gt;</literal> element to point
to the <literal>CodeConfigBootstrap.xml</literal> file instead of the
<literal>controllers.xml</literal> file as before.</para>
<para>In addition, the section name for the
<literal>&lt;parsers&gt;</literal> element must be registered with the
<literal>&lt;spring&gt;</literal> sectionGroup as shown here so that the
element can be properly interpreted:</para>
<programlisting language="xml"> &lt;configSections&gt;
&lt;sectionGroup name="spring"&gt;
&lt;section name="context" type="Spring.Context.Support.MvcContextHandler, Spring.Web.Mvc"/&gt;
&lt;section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core"/&gt;
&lt;/sectionGroup&gt;
&lt;!--
remainder of configSections element here...
--&gt;
&lt;/configSections&gt;</programlisting>
</section>
<section>
<title>Replacing controllers.xml with CodeConfigBootstrap.xml</title>
<para>Where before the <literal>controllers.xml</literal> file contained
all of the XML-based object definitions for the controllers for our MVC
application, in this case the <literal>CodeConfigBootstrap.xml</literal>
merely contains the single instruction telling the context to use
CodeConfig:</para>
<programlisting language="xml">&lt;?xml version="1.0" encoding="utf-8" ?&gt;
&lt;objects xmlns="http://www.springframework.net"
xmlns:context="http://www.springframework.net/context"&gt;
&lt;context:code-config/&gt;
&lt;/objects&gt; </programlisting>
<para>In this case, the single
<literal>&lt;context:code-config/&gt;</literal> element is sufficient to
tell the context to perform scanning for its object definition
metadata.</para>
</section>
<section>
<title>Introducing the ControllerConfiguration CodeConfig class</title>
<para>The final modification needed to migrate the sample application to
rely upon CodeConfig is to author the classes that will provide the
Object Definitions for the Application Context to discover when
performing the scan. In the case of this simple application, this is
encapsulated in the single new
<literal>ControllerConfiguration</literal> class as shown in the
following:</para>
<programlisting language="csharp"> [Configuration]
public class ControllerConfiguration
{
[ObjectDef]
[Scope(ObjectScope.Prototype)]
public virtual HomeController HomeController()
{
HomeController controller = new HomeController();
controller.Message = "Welcome to ASP.NET MVC powered by Spring.NET (Code-Config)!";
return controller;
}
}</programlisting>
<para>Using a combination of <literal><link
linkend="configuration-attribute-reference">[Configuration]</link></literal>
and <link
linkend="objectdef-attribute-reference"><literal>[ObjectDef]</literal></link>
attributes, this class faithfully reproduces the same Object Definition
configuration as had been described by the
<literal>controllers.xml</literal> file in the iniitial sample
application as delivered with the main Spring.NET Framework
download.</para>
</section>
</section>
<section xml:id="sample-migration-codeconfig">
<title>Spring.CodeConfig.Migration</title>
<para>This sample demonstates a step-by-step detailed examination of the
process of migrating an XML-configuration based solution to use CodeConfig
exclusively and is described in detail in <link
linkend="migration-example">the Introduction section</link> of the
reference documents.</para>
</section>
</chapter>

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -6392,17 +6392,8 @@ ctx.Refresh();</programlisting>
RootObjectDefinition, etc. Note a web version of this application class
has not yet been implemented.</para>
<para>An example, with a<emphasis> yet to be created</emphasis> DLL
scanner, that would get configuration metadata from the .dll named
MyAssembly.dll located in the runtime path, would look something like
this</para>
<programlisting language="csharp">GenericApplicationContext ctx = new GenericApplicationContext();
ObjectDefinitionScanner scanner = new ObjectDefinitionScanner(ctx);
scanner.scan("MyAssembly.dll");
ctx.refresh();</programlisting>
<para>Refer to the Spring API documentation for more information.</para>
<para>For a an even richer XML-free configuration experience, refer
instead to the Spring CodeConfig approach.</para>
</sect1>
<sect1 xml:id="objects-servicelocator">