Initial import!
BIN
doc/reference/images/admons/Thumbs.db
Normal file
BIN
doc/reference/images/admons/blank.png
Normal file
|
After Width: | Height: | Size: 374 B |
BIN
doc/reference/images/admons/caution.gif
Normal file
|
After Width: | Height: | Size: 743 B |
BIN
doc/reference/images/admons/caution.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
doc/reference/images/admons/caution.tif
Normal file
BIN
doc/reference/images/admons/draft.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
doc/reference/images/admons/home.gif
Normal file
|
After Width: | Height: | Size: 321 B |
BIN
doc/reference/images/admons/home.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
doc/reference/images/admons/important.gif
Normal file
|
After Width: | Height: | Size: 1003 B |
BIN
doc/reference/images/admons/important.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
doc/reference/images/admons/important.tif
Normal file
BIN
doc/reference/images/admons/next.gif
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
doc/reference/images/admons/next.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
doc/reference/images/admons/note.gif
Normal file
|
After Width: | Height: | Size: 580 B |
BIN
doc/reference/images/admons/note.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
doc/reference/images/admons/note.tif
Normal file
BIN
doc/reference/images/admons/prev.gif
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
doc/reference/images/admons/prev.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
doc/reference/images/admons/tip.gif
Normal file
|
After Width: | Height: | Size: 598 B |
BIN
doc/reference/images/admons/tip.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
doc/reference/images/admons/tip.tif
Normal file
BIN
doc/reference/images/admons/toc-blank.png
Normal file
|
After Width: | Height: | Size: 318 B |
BIN
doc/reference/images/admons/toc-minus.png
Normal file
|
After Width: | Height: | Size: 259 B |
BIN
doc/reference/images/admons/toc-plus.png
Normal file
|
After Width: | Height: | Size: 264 B |
BIN
doc/reference/images/admons/up.gif
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
doc/reference/images/admons/up.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
doc/reference/images/admons/warning.gif
Normal file
|
After Width: | Height: | Size: 743 B |
BIN
doc/reference/images/admons/warning.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
doc/reference/images/admons/warning.tif
Normal file
BIN
doc/reference/images/callouts/1.gif
Normal file
|
After Width: | Height: | Size: 968 B |
BIN
doc/reference/images/callouts/1.png
Normal file
|
After Width: | Height: | Size: 329 B |
BIN
doc/reference/images/callouts/10.gif
Normal file
|
After Width: | Height: | Size: 968 B |
BIN
doc/reference/images/callouts/10.png
Normal file
|
After Width: | Height: | Size: 361 B |
BIN
doc/reference/images/callouts/11.gif
Normal file
|
After Width: | Height: | Size: 244 B |
BIN
doc/reference/images/callouts/11.png
Normal file
|
After Width: | Height: | Size: 565 B |
BIN
doc/reference/images/callouts/12.gif
Normal file
|
After Width: | Height: | Size: 244 B |
BIN
doc/reference/images/callouts/12.png
Normal file
|
After Width: | Height: | Size: 617 B |
BIN
doc/reference/images/callouts/13.gif
Normal file
|
After Width: | Height: | Size: 244 B |
BIN
doc/reference/images/callouts/13.png
Normal file
|
After Width: | Height: | Size: 623 B |
BIN
doc/reference/images/callouts/14.gif
Normal file
|
After Width: | Height: | Size: 244 B |
BIN
doc/reference/images/callouts/14.png
Normal file
|
After Width: | Height: | Size: 411 B |
BIN
doc/reference/images/callouts/15.gif
Normal file
|
After Width: | Height: | Size: 244 B |
BIN
doc/reference/images/callouts/15.png
Normal file
|
After Width: | Height: | Size: 640 B |
BIN
doc/reference/images/callouts/2.gif
Normal file
|
After Width: | Height: | Size: 968 B |
BIN
doc/reference/images/callouts/2.png
Normal file
|
After Width: | Height: | Size: 353 B |
BIN
doc/reference/images/callouts/3.gif
Normal file
|
After Width: | Height: | Size: 968 B |
BIN
doc/reference/images/callouts/3.png
Normal file
|
After Width: | Height: | Size: 350 B |
BIN
doc/reference/images/callouts/4.gif
Normal file
|
After Width: | Height: | Size: 968 B |
BIN
doc/reference/images/callouts/4.png
Normal file
|
After Width: | Height: | Size: 345 B |
BIN
doc/reference/images/callouts/5.gif
Normal file
|
After Width: | Height: | Size: 968 B |
BIN
doc/reference/images/callouts/5.png
Normal file
|
After Width: | Height: | Size: 348 B |
BIN
doc/reference/images/callouts/6.gif
Normal file
|
After Width: | Height: | Size: 968 B |
BIN
doc/reference/images/callouts/6.png
Normal file
|
After Width: | Height: | Size: 355 B |
BIN
doc/reference/images/callouts/7.gif
Normal file
|
After Width: | Height: | Size: 968 B |
BIN
doc/reference/images/callouts/7.png
Normal file
|
After Width: | Height: | Size: 344 B |
BIN
doc/reference/images/callouts/8.gif
Normal file
|
After Width: | Height: | Size: 968 B |
BIN
doc/reference/images/callouts/8.png
Normal file
|
After Width: | Height: | Size: 357 B |
BIN
doc/reference/images/callouts/9.gif
Normal file
|
After Width: | Height: | Size: 968 B |
BIN
doc/reference/images/callouts/9.png
Normal file
|
After Width: | Height: | Size: 357 B |
BIN
doc/reference/images/callouts/Thumbs.db
Normal file
2307
doc/reference/src/ado.xml
Normal file
154
doc/reference/src/ajax.xml
Normal file
@@ -0,0 +1,154 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
-->
|
||||
<chapter id="ajax">
|
||||
<title>ASP.NET AJAX</title>
|
||||
|
||||
<sect1 id="introduction-ajax">
|
||||
<title>Introduction</title>
|
||||
|
||||
<para>Spring's ASP.NET AJAX integration allows for a plain .NET object
|
||||
(PONO), that is one that doesn't have any attributes or special base
|
||||
classes, to be exported as a web service, configured via dependency
|
||||
injection, 'decorated' by applying AOP, and then exposed to client side
|
||||
JavaScript.</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="webServices">
|
||||
<title>Web Services</title>
|
||||
|
||||
<para>Spring.NET, and particularly Spring.Web, improved <ulink
|
||||
url="http://www.springframework.net/doc-latest/reference/html/webservices.html">support
|
||||
for web services</ulink> in .NET with the
|
||||
<classname>WebServiceExporter</classname>. Exporting of an ordinary plain
|
||||
.NET object as a web service is achieved by registering a custom
|
||||
implementation of the <classname>WebServiceHandlerFactory</classname>
|
||||
class as the HTTP handler for <literal>*.asmx</literal> requests.</para>
|
||||
|
||||
<para><ulink
|
||||
url="http://www.springframework.net/doc-latest/reference/html/webservices.html">Microsoft
|
||||
ASP.NET AJAX</ulink> introduced a new HTTP handler
|
||||
<classname>System.Web.Script.Services.ScriptHandlerFactory</classname> to
|
||||
allow a Web Service to be invoked from the browser by using
|
||||
JavaScript.</para>
|
||||
|
||||
<para>Spring's integration allows for both Spring.Web and ASP.NET AJAX
|
||||
functionality to be used together by creating a new HTTP handler.</para>
|
||||
|
||||
<sect2 id="exposingWebServices">
|
||||
<title>Exposing Web Services</title>
|
||||
|
||||
<para>The <classname>WebServiceExporter</classname> combined with the
|
||||
new HTTP handler exposes PONOs as Web Services in your ASP.NET AJAX
|
||||
application.</para>
|
||||
|
||||
<para>In order for a Web service to be accessed from script, the
|
||||
<classname>WebServiceExporter</classname> should decorate the Web
|
||||
Service class with the <classname>ScriptServiceAttribute</classname>.
|
||||
The code below is taken from the sample application
|
||||
Spring.Web.Extensions.Sample, aka the 'AJAX' shortcut in the
|
||||
installation. : <programlisting>
|
||||
<object id="ContactWebService" type="Spring.Web.Services.WebServiceExporter, Spring.Web">
|
||||
<property name="TargetName" value="ContactService"/>
|
||||
<property name="Namespace" value="http://Spring.Examples.Atlas/ContactService"/>
|
||||
<property name="Description" value="Contact Web Services"/>
|
||||
<property name="TypeAttributes">
|
||||
<list>
|
||||
<object type="System.Web.Script.Services.ScriptServiceAttribute, System.Web.Extensions"/>
|
||||
</list>
|
||||
</property>
|
||||
</object>
|
||||
|
||||
</programlisting></para>
|
||||
|
||||
<para>All that one needs to do in order to use the
|
||||
<classname>WebServiceExporter</classname> is:</para>
|
||||
|
||||
<para><emphasis> 1. Configure the Web.config file of your ASP.NET AJAX
|
||||
application as a Spring.Web application. </emphasis> <programlisting>
|
||||
<sectionGroup name="spring">
|
||||
<section name="context" type="Spring.Context.Support.WebContextHandler, Spring.Web"/>
|
||||
</sectionGroup>
|
||||
|
||||
</programlisting> <programlisting>
|
||||
<spring>
|
||||
<context>
|
||||
<resource uri="~/Spring.config"/>
|
||||
</context>
|
||||
</spring>
|
||||
|
||||
</programlisting></para>
|
||||
|
||||
<para><emphasis> 2. Register the HTTP handler and the Spring HttpModule
|
||||
under the <literal>system.web</literal> section. </emphasis>
|
||||
<programlisting>
|
||||
<httpHandlers>
|
||||
<remove verb="*" path="*.asmx"/>
|
||||
<add verb="*" path="*.asmx" validate="false" type="Spring.Web.Script.Services.ScriptHandlerFactory, Spring.Web.Extensions"/>
|
||||
<add verb="*" path="*_AppService.axd" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="false"/>
|
||||
</httpHandlers>
|
||||
|
||||
<httpModules>
|
||||
<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add name="SpringModule" type="Spring.Context.Support.WebSupportModule, Spring.Web"/>
|
||||
</httpModules>
|
||||
|
||||
</programlisting></para>
|
||||
|
||||
<para><emphasis> 3. Register the HTTP handler and the Spring HttpModule
|
||||
under <literal>system.webServer</literal> section. </emphasis>
|
||||
<programlisting>
|
||||
<modules>
|
||||
<add name="ScriptModule" preCondition="integratedMode" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add name="SpringModule" type="Spring.Context.Support.WebSupportModule, Spring.Web"/>
|
||||
</modules>
|
||||
<handlers>
|
||||
<remove name="WebServiceHandlerFactory-Integrated" />
|
||||
<add name="ScriptHandlerFactory" verb="*" path="*.asmx" preCondition="integratedMode"
|
||||
type="Spring.Web.Script.Services.ScriptHandlerFactory, Spring.Web.Extensions"/>
|
||||
<add name="ScriptHandlerFactoryAppServices" verb="*" path="*_AppService.axd" preCondition="integratedMode"
|
||||
type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add name="ScriptResource" preCondition="integratedMode" verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||
</handlers>
|
||||
|
||||
</programlisting></para>
|
||||
|
||||
<para>You can find a full Web.config file in the example that comes with
|
||||
this integration.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="callingWebServices">
|
||||
<title>Calling Web Services by using JavaScript</title>
|
||||
|
||||
<para>A proxy class is generated for each Web Service. Calls to Web
|
||||
Services methods are made by using this proxy class. When using the
|
||||
<classname>WebServiceExporter</classname>, the name of the proxy class
|
||||
is equal to the <classname>WebServiceExporter</classname>'s id.
|
||||
<programlisting>
|
||||
// This function calls the Contact Web service method
|
||||
// passing simple type parameters and the callback function
|
||||
function GetEmails(prefix, count)
|
||||
{
|
||||
ContactWebService.GetEmails(prefix, count, GetEmailsOnSucceeded);
|
||||
}
|
||||
|
||||
</programlisting></para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
</chapter>
|
||||
733
doc/reference/src/aop-aspect-library.xml
Normal file
@@ -0,0 +1,733 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<chapter id="aop-aspect-library">
|
||||
<title>Aspect Library</title>
|
||||
|
||||
<sect1 id="aop-library-introduction">
|
||||
<title>Introduction</title>
|
||||
|
||||
<para>Spring provides several aspects in the distribution. The most
|
||||
popular of which is transactional advice, located in the Spring.Data
|
||||
module. However, the aspects that are documented in this section are those
|
||||
contained within the Spring.Aop module itself. The aspects in within
|
||||
Spring.Aop.dll are Caching, Exception Handling, Logging, Retry, and
|
||||
Parameter Validation. Other traditional advice types such as validation,
|
||||
security, and thread management, will be included in a future
|
||||
release.</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="caching-aspect">
|
||||
<title>Caching</title>
|
||||
|
||||
<para>Caching the return value of a method or the value of a method
|
||||
parameter is a common approach to increase application performance.
|
||||
Application performance is increased with effective use of caching since
|
||||
layers in the application that are closer to the user can return
|
||||
information within their own layer as compared to making more expensive
|
||||
calls to retrieve that information from a lower, and more slow, layer such
|
||||
as a database or a web service. Caching also can help in terms of
|
||||
application scalability, which is generally the more important
|
||||
concern.</para>
|
||||
|
||||
<para>The caching support in Spring.NET consists of base cache interfaces
|
||||
that can be used to specify a specific storage implementation of the cache
|
||||
and also an aspect that determines where to apply the caching
|
||||
functionality and its configuration.</para>
|
||||
|
||||
<para>The base cache interface that any cache implementation should
|
||||
implement is <classname>Spring.Caching.ICache</classname> located in
|
||||
<classname>Spring.Core.</classname> Two implementations are provided,
|
||||
<classname>Spring.Caching.AspNetCache </classname>located in
|
||||
<classname>Spring.Web</classname> which stores cache entries within an
|
||||
ASP.NET cache and a simple implementation,
|
||||
<classname>Spring.Caching.NonExpiringCache</classname> that stores cache
|
||||
entries in memory and never expires these entries. Custom implementations
|
||||
based on 3rd party implementations, such as Oracle Coherence, or
|
||||
memcached, can be used by implementing the <literal>ICache</literal>
|
||||
interface.</para>
|
||||
|
||||
<para>The cache aspect is
|
||||
<literal>Spring.Aspects.Cache.CacheAspect</literal> located in
|
||||
<literal>Spring.Aop</literal>. It consists of three pieces of
|
||||
functionality, the ability to cache return values, method parameters, and
|
||||
explicit eviction of an item from the cache. The aspect currently relies
|
||||
on using attributes to specify the pointcut as well as the behavior, much
|
||||
like the transactional aspect. Future versions will allow for external
|
||||
configuration of the behavior so you can apply caching to a code base
|
||||
without needing to use attributes in the code.</para>
|
||||
|
||||
<para>The following attributes are available</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para><literal>CacheResult - used to cache the return
|
||||
value</literal></para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>CacheResultItems - used when returning a collection as
|
||||
a return value </literal></para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>CacheParameter - used to cache a method
|
||||
parameter</literal></para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>InvalidateCache</literal> - used to indicate one or
|
||||
more cache items should be invalidated.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>Each <classname>CacheResult</classname>,
|
||||
<classname>CacheResultItems</classname>, and
|
||||
<classname>CacheParameter</classname> attributes define the following
|
||||
properties.</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para><literal>CacheName</literal> - the name of the cache
|
||||
implementation to use</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>Key</literal> - a string representing a Spring
|
||||
Expression Language (SpEL) expression used as the key in the
|
||||
cache.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>Condition</literal> - a SpEL expression that should be
|
||||
evaluated in order to determine whether the item should be
|
||||
cached.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>TimeToLive</literal> - The amount of time an object
|
||||
should remain in the cache (in seconds).</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>The <classname>InvalidateCache</classname> attribute has properties
|
||||
for the CacheName, the Key as well as the Condition, with the same
|
||||
meanings as listed previously.</para>
|
||||
|
||||
<para>Each <classname>ICache</classname> implementation will have
|
||||
properties that are specific to a caching technology. In the case of
|
||||
<classname>AspNetCache</classname>, the two important properties to
|
||||
configure are:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para><literal>SlidingExperation</literal> - If this property value is
|
||||
set to true, every time the marked object is accessed it's TimeToLive
|
||||
value is reset to its original value</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>Priority</literal> - the cache item priority
|
||||
controlling how likely an object is to be removed from an associated
|
||||
cache when the cache is being purged.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>TimeToLive</literal> - The amount of time an object
|
||||
should remain in the cache (in seconds).</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>The values of the Priority enumeration are</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para><literal>Low</literal> - low likelihood of deletion when cache
|
||||
is purged.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>Normal</literal> - default priority for deletion when
|
||||
cache is purged.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>High</literal> - high likelihood of deletion when cache
|
||||
is purged.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>NotRemovable</literal> - cache item not deleted when
|
||||
cache is purged.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>An important element of the applying these attributes is the use of
|
||||
the expression language that allows for calling context information to
|
||||
drive the caching actions. Here is an example taken from the Spring Air
|
||||
sample application of the AirportDao implementation that implements an
|
||||
interface with the method GetAirport(long id).</para>
|
||||
|
||||
<programlisting> [CacheResult("AspNetCache", "'Airport.Id=' + #id", TimeToLive = "0:1:0")]
|
||||
public Airport GetAirport(long id)
|
||||
{
|
||||
// implementation not shown...
|
||||
}
|
||||
</programlisting>
|
||||
|
||||
<para>The first parameter is the cache name. The second string parameter
|
||||
is the cache key and is a string expression that incorporates the argument
|
||||
passed into the method, the id. The method parameter names are exposed as
|
||||
variables to the key expression. If you do not specify a key, then all the
|
||||
parameter values will be used to cache the returned value. The expression
|
||||
may also call out to other objects in the Spring container allowing for a
|
||||
more complex key algorithm to be encapsulated. The end result is that the
|
||||
Airport object is cached by id for 60 seconds in a cache named
|
||||
AspNetCache. The TimetoLive property could also have been specified on the
|
||||
configuration of the AspNetCache object.</para>
|
||||
|
||||
<para>The configuration to enable the caching aspect is shown below</para>
|
||||
|
||||
<programlisting> <object id="CacheAspect" type="Spring.Aspects.Cache.CacheAspect, Spring.Aop"/>
|
||||
<object id="AspNetCache" type="Spring.Caching.AspNetCache, Spring.Web">
|
||||
<property name="SlidingExpiration" value="true"/>
|
||||
<property name="Priority" value="CachePriority.Low"/>
|
||||
<property name="TimeToLive" value="00:02:00"/>
|
||||
</object>
|
||||
|
||||
|
||||
<!-- Apply aspects to DAOs -->
|
||||
<object type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxyCreator, Spring.Aop">
|
||||
<property name="ObjectNames">
|
||||
<list>
|
||||
<value>*Dao</value>
|
||||
</list>
|
||||
</property>
|
||||
<property name="InterceptorNames">
|
||||
<list>
|
||||
<value>CacheAspect</value>
|
||||
</list>
|
||||
</property>
|
||||
</object></programlisting>
|
||||
|
||||
<para>in this example an <classname>ObjectNameAutoProxyCreator</classname>
|
||||
was used to apply the cache aspect to objects that have Dao in their name.
|
||||
The AspNetCache setting for TimeToLive will override the TimeToLive value
|
||||
set at the method level via the attribute.</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="exception-aspect">
|
||||
<title>Exception Handling</title>
|
||||
|
||||
<para>In some cases existing code can be easily adopted to a simple error
|
||||
handling strategy that can perform one of the following actions</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>translations - either wrap the thrown exception inside a new one
|
||||
or replace it with a new exception type (no inner exception is
|
||||
set).</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>return value - the exception is ignored and a return value for
|
||||
the method is provided instead</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>swallow - the exception is ignored.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>The applicability of general exception handling advice depends
|
||||
greatly on how tangled the code is regarding access to local variables
|
||||
that may form part of the exception. Once you get familiar with the
|
||||
feature set of Spring declarative exception handling advice you should
|
||||
evaluate where it may be effectively applied in your code base. It is
|
||||
worth noting that you can still chain together multiple pieces of
|
||||
exception handling advice allowing you to mix the declarative approach
|
||||
shown in this section with the traditional inheritance based approach,
|
||||
i.e. implementing IThrowsAdvice or IMethodInterceptor.</para>
|
||||
|
||||
<para>Declarative exception handling is expressed in the form of a
|
||||
mini-language relevant to the domain at hand, exception handling. This
|
||||
could be referred to as a Domain Specific Language (DSL). Here is a simple
|
||||
example, which should hopefully be self explanatory.</para>
|
||||
|
||||
<para><programlisting><object name="exceptionHandlingAdvice" type="Spring.Aspects.Exceptions.ExceptionHandlerAdvice, Spring.Aop">
|
||||
<property name="exceptionHandlers">
|
||||
<list>
|
||||
<value><emphasis role="bold">on exception name ArithmeticException wrap System.InvalidOperationException</emphasis></value>
|
||||
</list>
|
||||
</property>
|
||||
</object></programlisting>What this is instructing the advice to do is
|
||||
the following bit of code when an ArithmeticException is thrown, throw new
|
||||
System.InvalidOperationException("Wrapped ArithmeticException", e), where
|
||||
e is the original ArithmeticException. The default message, "Wrapped
|
||||
ArithmethicException" is automatically appended. You may however specify
|
||||
the message used in the newly thrown exception as shown below</para>
|
||||
|
||||
<programlisting>on exception name ArithmeticException wrap System.InvalidOperationException 'My Message'</programlisting>
|
||||
|
||||
<para>Similarly, if you would rather replace the exception, that is do not
|
||||
nest one inside the other, you can use the following syntax</para>
|
||||
|
||||
<programlisting>on exception name ArithmeticException replace System.InvalidOperationException
|
||||
|
||||
or
|
||||
|
||||
on exception name ArithmeticException replace System.InvalidOperationException 'My Message'</programlisting>
|
||||
|
||||
<para>Both wrap and replace are special cases of the more general
|
||||
translate action. An example of a translate expression is shown
|
||||
below</para>
|
||||
|
||||
<para><programlisting>on exception name ArithmeticException translate new System.InvalidOperationException('My Message, Method Name ' + #method.Name, #e)</programlisting>What
|
||||
we see here after the translate keyword is text that will be passed into
|
||||
Spring's expression language (SpEL) for evaluation. Refer to the chapter
|
||||
on the <link linkend="expression">expression language</link> for more
|
||||
details. One important feature of the expression evaluation is the
|
||||
availability of variables relating to the calling context when the
|
||||
exception was thrown. These are</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>method - the MethodInfo object corresponding to the method that
|
||||
threw the exception</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>args - the argument array to the method that threw the
|
||||
exception, signature is object[]</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>target - the AOP target object instance.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>e - the thrown exception</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>You can invoke methods on these variables, prefixed by a '#' in the
|
||||
expression. This gives you the flexibility to call special purpose
|
||||
constructors that can have any piece of information accessible via the
|
||||
above variables, or even other external data through the use of SpEL's
|
||||
ability to reference objects within the Spring container.</para>
|
||||
|
||||
<para>You may also choose to 'swallow' the exception or to return a
|
||||
specific return value, for example</para>
|
||||
|
||||
<programlisting>on exception name ArithmeticException swallow
|
||||
|
||||
|
||||
or
|
||||
|
||||
|
||||
on exception name ArithmeticException return 12</programlisting>
|
||||
|
||||
<para>You may also simply log the exception</para>
|
||||
|
||||
<programlisting>on exception name ArithmeticException,ArgumentException log 'My Message, Method Name ' + #method.Name</programlisting>
|
||||
|
||||
<para>Here we see that a comma delimited list of exception names can be
|
||||
specified.</para>
|
||||
|
||||
<para>The logging is performed using the Commons.Logging library that
|
||||
provides an abstraction over the underlying logging implementation.
|
||||
Logging is currently at the debug level with a logger name of
|
||||
"LogExceptionHandler" The ability to specify these values will be a future
|
||||
enhancement and likely via a syntax resembling a constructor for the
|
||||
action, i.e. log(Debug,"LoggerName").</para>
|
||||
|
||||
<para>Multiple exception handling statements can be specified within the
|
||||
<list> shown above. The processing flow is on exception, the name of
|
||||
the exception listed in the statement is compared to the thrown exception
|
||||
to see if there is a match. A comma separated list of exceptions can be
|
||||
used to group together the same action taken for different exception
|
||||
names. If the action to take is logging, then the logging action is
|
||||
performed and the search for other matching exception names continues. For
|
||||
all other actions, namely translate, wrap, replace, swallow, return, once
|
||||
an exception handler is matched, those in the chain are no longer
|
||||
evaluated. Note, do not confuse this handler chain with the general advice
|
||||
AOP advice chain. For translate, wrap, and replace actions a SpEL
|
||||
expression is created and used to instantiate a new exception (in addition
|
||||
to any other processing that may occur when evaluating the expression)
|
||||
which is then thrown.</para>
|
||||
|
||||
<para>The exception handling DSL also supports the ability to provide a
|
||||
SpEL boolean expression to determine if the advice will apply instead of
|
||||
just filtering by the expression name. For example, the following is the
|
||||
equivalent to the first example based on exception names but compares the
|
||||
specific type of the exception thrown</para>
|
||||
|
||||
<programlisting><emphasis role="bold">on exception (#e is T(System.ArithmeticException))</emphasis> wrap System.InvalidOperationException</programlisting>
|
||||
|
||||
<para>The syntax use is 'on exception (SpEL boolean expression)' and
|
||||
inside the expression you have access to the variables of the calling
|
||||
context listed before, i.e. method, args, target, and e. This can be
|
||||
useful to implement a small amount of conditional logic, such as checking
|
||||
for a specific error number in an exception, i.e. <literal>(#e is
|
||||
T(System.Data.SqlException) && #e.Errors[0].Number in
|
||||
{156,170,207,208})</literal>, to catch and translate bad grammar codes in
|
||||
a SqlException.</para>
|
||||
|
||||
<para>While the examples given above are toy examples, they could just as
|
||||
easily be changed to convert your application specific exceptions. If you
|
||||
find yourself pushing the limits of using SpEL expressions, you will
|
||||
likely be better off creating your own custom aspect class instead of a
|
||||
scripting approach.</para>
|
||||
|
||||
<sect2>
|
||||
<title>Language Reference</title>
|
||||
|
||||
<para>The general syntax of the language is</para>
|
||||
|
||||
<para><literal>on exception name [ExceptionName1,ExceptionName2,...]
|
||||
[action] [SpEL expression]</literal></para>
|
||||
|
||||
<para>or</para>
|
||||
|
||||
<para><literal>on exception (SpEL boolean expression) [action] [SpEL
|
||||
expression]</literal></para>
|
||||
|
||||
<para>The exception names are required as well as the action. The valid
|
||||
actions are</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>log</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>translate</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>wrap</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>replace</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>return</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>swallow</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>The form of the expression depends on the action. For logging, the
|
||||
entire string is taken as the SpEL expression to log. Translate expects
|
||||
an exception to be returned from evaluation the SpEL expression. Wrap
|
||||
and replace are shorthand for the translate action. For wrap and replace
|
||||
you specify the exception name and the message to pass into the standard
|
||||
exception constructors (string, exception) and (string). The exception
|
||||
name can be a partial or fully qualified name. Spring will attempt to
|
||||
resolve the typename across all referenced assemblies. You may also
|
||||
register type aliases for use with SpEL in the standard manner with
|
||||
Spring.NET and those will be accessible from within the exception
|
||||
handling expression.</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="logging-aspect">
|
||||
<title>Logging</title>
|
||||
|
||||
<para>The logging advice lets you log the information on method entry,
|
||||
exit and thrown exception (if any). The implementation is based on the
|
||||
logging library, <link linkend="???">Common.Logging</link>, that provides
|
||||
portability across different logging libraries. There are a number of
|
||||
configuration options available, listed below</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>LogUniqueIdentifier</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>LogExecutionTime</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>LogMethodArguments</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>LogReturnValue</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Separator</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>LogLevel</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>You declare the logging advice in IoC container with the following
|
||||
XML fragment. Alternatively, you can use the class
|
||||
<classname>SimpleLoggingAdvice</classname> programatically.</para>
|
||||
|
||||
<programlisting><object name="loggingAdvice" type="Spring.Aspects.Logging.SimpleLoggingAdvice, Spring.Aop">
|
||||
<property name="logUniqueIdentifier" value="true"/>
|
||||
<property name="logExecutionTime" value="true"/>
|
||||
<property name="logMethodArguments" value="true"/>
|
||||
<property name="LogReturnValue" value="true"/>
|
||||
|
||||
<property name="Separator" value=";"/>
|
||||
<property name="LogLevel" value="Info"/>
|
||||
|
||||
|
||||
<property name="HideProxyTypeNames" value="true"/>
|
||||
<property name="UseDynamicLogger" value="true"/>
|
||||
</object></programlisting>
|
||||
|
||||
<para>
|
||||
The default values for LogUniqueIdentifier, LogExecutionTime, LogMethodArguments and
|
||||
LogReturnValue are false. The default separator value is ", " and the
|
||||
default log level is Common.Logging's LogLevel.Trace.</para>
|
||||
|
||||
<para>You can set the name of the logger with the property
|
||||
<property>LoggerName</property>, for example "DataAccessLayer" for a
|
||||
logging advice that would be applied across the all the classes in the
|
||||
data access layer. That works well when using a 'category' style of
|
||||
logging. If you do not set the <property>LoggerName</property> property,
|
||||
then the type name of the logging advice is used as the logging name.
|
||||
Another approach to logging is to log based on the type of the object
|
||||
being called, the target type. Since often this is a proxy class with a
|
||||
relatively meaningless name, the property
|
||||
<property>HideProxyTypeNames</property> can be set to true to show the
|
||||
true target type and not the proxy type.</para>
|
||||
|
||||
<para>To further extend the functionality of the
|
||||
<classname>SimpleLoggingAdvice</classname> you can subclass
|
||||
<classname>SimpleLoggingAdvice</classname> and override the methods</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para><literal>string GetEntryMessage(IMethodInvocation invocation,
|
||||
string idString)</literal></para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>string GetExceptionMessage(IMethodInvocation
|
||||
invocation, Exception e, TimeSpan executionTimeSpan, string
|
||||
idString)</literal></para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>string GetExitMessage(IMethodInvocation invocation,
|
||||
object returnValue, TimeSpan executionTimeSpan, string
|
||||
idString)</literal></para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>The default implementation to calculate a unique identifier is to
|
||||
use a GUID. You can alter this behavior by overriding the method
|
||||
<literal>string CreateUniqueIdentifier()</literal>. The
|
||||
<classname>SimpleLoggingAdvice</classname> class inherits from
|
||||
<classname>AbstractLoggingAdvice</classname>, which has the abstract
|
||||
method <literal>object InvokeUnderLog(IMethodInvocation invocation, ILog
|
||||
log)</literal> and you can also override the method <literal>ILog
|
||||
GetLoggerForInvocation(IMethodInvocation invocation)</literal> to
|
||||
customize the logger instance used for logging. Refer to the SDK
|
||||
documentation for more details on subclassing
|
||||
<classname>AbstractLoggingAdvice</classname>.</para>
|
||||
|
||||
<para>As an example of the Logging advice's output, adding the advice to
|
||||
the method</para>
|
||||
|
||||
<programlisting>public int Bark(string message, int[] luckyNumbers)
|
||||
{
|
||||
return 4;
|
||||
}</programlisting>
|
||||
|
||||
<para>And calling Bark("hello", new int[]{1, 2, 3} ), results in the
|
||||
following output</para>
|
||||
|
||||
<programlisting>Entering Bark, 5d2bad47-62cd-435b-8de7-91f12b7f433e, message=hello; luckyNumbers=System.Int32[]
|
||||
|
||||
Exiting Bark, 5d2bad47-62cd-435b-8de7-91f12b7f433e, 30453.125 ms, return=4</programlisting>
|
||||
|
||||
<para>The method parameters values are obtained using the ToString()
|
||||
method. If you would like to have an alternate implementation, say to view
|
||||
some values in an array, override the method string
|
||||
GetMethodArgumentAsString(IMethodInvocation invocation).</para>
|
||||
|
||||
<para>The Spring 1.2 release will have an additional logging advice
|
||||
implementation that leverages the Spring Expression Language to further
|
||||
customize the content of the logging messages via simple configuration
|
||||
using similar syntax to the retry and exception handling advice.</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="retry-aspect">
|
||||
<title>Retry</title>
|
||||
|
||||
<para>When making a distributed call it is often a common requirement to
|
||||
be able to retry the method invocation if there was an exception.
|
||||
Typically the exception will be due to a communication issue that is
|
||||
intermittent and retrying over a period of time will likely result in a
|
||||
successful invocation. When applying retry advice it is important to know
|
||||
if making two calls to the remote service will cause side effects.
|
||||
Generally speaking, the method being invoked should be <ulink
|
||||
url="http://en.wikipedia.org/wiki/Idempotent#Computer_Science">idempotent</ulink>,
|
||||
that is, it is safe to call multiple times.</para>
|
||||
|
||||
<para>The retry advice is specified using a little language, i.e a DSL. A
|
||||
simple example is shown below</para>
|
||||
|
||||
<programlisting>on exception name ArithmeticException retry 3x delay 1s</programlisting>
|
||||
|
||||
<para>The meaning is: when an exception that has 'ArithmeticException' in
|
||||
its type name is thrown, retry the invocation up to 3 times and delay for
|
||||
1 second between each retry event.</para>
|
||||
|
||||
<para>You can also provide a SpEL (Spring Expression Language) expression
|
||||
that calculates the time interval to sleep between each retry event. The
|
||||
syntax for this is shown below</para>
|
||||
|
||||
<programlisting>on exception name ArithmeticException retry 3x rate (1*#n + 0.5)</programlisting>
|
||||
|
||||
<para>As with the exception handling advice, you may also specify a
|
||||
boolean SpEL that must evaluate to true in order for the advice to apply.
|
||||
For example</para>
|
||||
|
||||
<programlisting>on exception (#e is T(System.ArithmeticException)) retry 3x delay 1s
|
||||
|
||||
|
||||
on exception (#e is T(System.ArithmeticException)) retry 3x rate (1*#n + 0.5)</programlisting>
|
||||
|
||||
<para>The time specified after the delay keyword is converted to a
|
||||
TimeSpan object using Spring's TimeSpanConverter. This supports setting
|
||||
the time as an integer + time unit. Time units are (d, h, m, s, ms)
|
||||
representing (days, hours, minutes, seconds, and milliseconds). For
|
||||
example; 1d = 1day, 5h = 5 hours etc. You can not specify a string such as
|
||||
'1d 5h'. The value that is calculated from the expression after the rate
|
||||
keyword is interpreted as a number of seconds. The power of using SpEL for
|
||||
the rate expression is that you can easily specify some exponential retry
|
||||
rate (a bigger delay for each retry attempt) or call out to a custom
|
||||
function developed for this purpose.</para>
|
||||
|
||||
<para>When using a SpEL expression for the filter condition or for the
|
||||
rate expression, the following variable are available</para>
|
||||
|
||||
<para><itemizedlist>
|
||||
<listitem>
|
||||
<para>method - the MethodInfo object corresponding to the method
|
||||
that threw the exception</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>args - the argument array to the method that threw the
|
||||
exception, signature is object[]</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>target - the AOP target object instance.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>e - the thrown exception</para>
|
||||
</listitem>
|
||||
</itemizedlist>You declare the advice in IoC container with the
|
||||
following XML fragment. Alternatively, you can use the
|
||||
<classname>RetryAdvice</classname> class programatically.</para>
|
||||
|
||||
<programlisting><object name="exceptionHandlingAdvice" type="Spring.Aspects.RetryAdvice, Spring.Aop">
|
||||
<property name="retryExpression" value="<emphasis role="bold">on exception name ArithmeticException retry 3x delay 1s</emphasis>"/>
|
||||
</object></programlisting>
|
||||
|
||||
<sect2>
|
||||
<title>Language Reference</title>
|
||||
|
||||
<para>The general syntax of the language is</para>
|
||||
|
||||
<para><literal>on exception name [ExceptionName1,ExceptionName2,...]
|
||||
retry [number of times]x [delay|rate] [delay time|SpEL rate
|
||||
expression]</literal></para>
|
||||
|
||||
<para>or</para>
|
||||
|
||||
<para><literal>on exception (SpEL boolean expression) retry [number of
|
||||
times]x [delay|rate] [delay time|SpELrate expression]</literal></para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="tx-aspect">
|
||||
<title>Transactions</title>
|
||||
|
||||
<para>The transaction aspect is more fully described in the section on
|
||||
<link linkend="transaction">transaction management</link>.</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="parameter-validation">
|
||||
<title>Parameter Validation</title>
|
||||
|
||||
<para>Spring provides a UI-agnostic <link linkend="validation">validation
|
||||
framework</link> in which you can declare validation rules, both
|
||||
progammatically and declaratively, and have those rules evaluated against
|
||||
an arbitrary .NET object. Spring provides additional support for rendering
|
||||
of validation errors within Spring's ASP.NET framework. (See the section
|
||||
on <link linkend="validation-aspnet-usage" os="">ASP.NET usage tips</link>
|
||||
for more information.) However, validation is not confined to the UI tier.
|
||||
It is a common task that occurs across most, if not all, applications
|
||||
layers. Validation that is performed in the UI layer is often repeated in
|
||||
the service layer, in order to be proactive in case non UI-based clients
|
||||
invoke the service layer. Validation rules completely different from those
|
||||
used in the UI layer may be used on the server side.</para>
|
||||
|
||||
<para>To address some of the common needs for validation on the server
|
||||
side, Spring provides parameter validation advice so that applies Spring's
|
||||
validation rules to the method parameters. The class
|
||||
<classname>ParameterValidationAdvice</classname> is used in conjunction
|
||||
with the <classname>Validated</classname> attribute to specify which
|
||||
validation rules are applied to method parameters. For example, to apply
|
||||
parameter validation to the method SuggestFlights in the BookingAgent
|
||||
class used in the <link linkend="springair">SpringAir sample
|
||||
application</link>, you would apply the <classname>Validated</classname>
|
||||
attribute to the method parameters as shown below.</para>
|
||||
|
||||
<programlisting>public FlightSuggestions SuggestFlights( [Validated("tripValidator")] Trip trip)
|
||||
{
|
||||
// unmodified implementation goes here
|
||||
}</programlisting>
|
||||
|
||||
<para>The <literal>Validated</literal> attribute takes a string name that
|
||||
specifies the name of the validation rule, i.e. the name of the IValidator
|
||||
object in the Spring application context. The
|
||||
<classname>Validated</classname> attribute is located in the namespace
|
||||
<literal>Spring.Validation</literal> of the <literal>Spring.Core</literal>
|
||||
assembly.</para>
|
||||
|
||||
<para>The configuration of the advice is to simply define the an instance
|
||||
of the <literal>ParameterValidationAdvice</literal> class and apply the
|
||||
advice, for example based on object names using an
|
||||
<classname>ObjectNameAutoProxyCreator</classname>, as shown below,</para>
|
||||
|
||||
<programlisting> <object id="<emphasis role="bold">validationAdvice</emphasis>" type="Spring.Aspects.Validation.ParameterValidationAdvice, Spring.Aop"/>
|
||||
|
||||
<object type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxyCreator, Spring.Aop">
|
||||
<property name="ObjectNames">
|
||||
<list>
|
||||
<value>bookingAgent</value>
|
||||
</list>
|
||||
</property>
|
||||
<property name="InterceptorNames">
|
||||
<list>
|
||||
<value><emphasis role="bold">validationAdvice</emphasis></value>
|
||||
</list>
|
||||
</property>
|
||||
</object></programlisting>
|
||||
|
||||
<para>When the advised method is invoked first the validation of each
|
||||
method parameter is performed. If all validation succeeds, then the method
|
||||
body is executed. If validation fails an exception of the type
|
||||
<classname>ValidationException</classname> is thrown and you can retrieve
|
||||
errors information from its property <literal>ValidationErrors</literal>.
|
||||
See the SDK documentation for details.</para>
|
||||
</sect1>
|
||||
</chapter>
|
||||
1099
doc/reference/src/aop-quickstart.xml
Normal file
2470
doc/reference/src/aop.xml
Normal file
34
doc/reference/src/background.xml
Normal file
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<chapter id="background">
|
||||
<title>Background information</title>
|
||||
|
||||
<sect1 id="background-ioc">
|
||||
<title>Inversion of Control</title>
|
||||
|
||||
<para>In early 2004, Martin Fowler asked the readers of his site: when
|
||||
talking about Inversion of Control: <emphasis>"the question, is what
|
||||
aspect of control are they inverting?"</emphasis>. After talking about the
|
||||
term Inversion of Control Martin suggests renaming the pattern, or at
|
||||
least giving it a more self-explanatory name, and starts to use the term
|
||||
<emphasis>Dependency Injection</emphasis>. His <ulink
|
||||
url="http://martinfowler.com/articles/injection.html">article</ulink>
|
||||
continues to explain some of the ideas behind this important software
|
||||
engineering principle.</para>
|
||||
|
||||
<para>Other references you may find useful are</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>Wikipedia Article - <ulink
|
||||
url="http://en.wikipedia.org/wiki/Dependency_injection">Dependency
|
||||
Injection</ulink></para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>CodeProject article - <ulink
|
||||
url="http://www.codeproject.com/cs/design/DependencyInjection.asp">Dependency
|
||||
Injection for Loose Coupling</ulink></para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</sect1>
|
||||
</chapter>
|
||||
325
doc/reference/src/dao.xml
Normal file
@@ -0,0 +1,325 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<chapter id="dao">
|
||||
<title>DAO support</title>
|
||||
|
||||
<section id="dao-introduction">
|
||||
<title>Introduction</title>
|
||||
|
||||
<para>Spring promotes the use of data access interfaces in your
|
||||
application architecture. These interfaces encapsulate the storage and
|
||||
retrieval of data and objects specific to your business domain without
|
||||
reference to a specific persistence API. Within a layered architecture,
|
||||
the service layer is typically responsible for coordinating responses to a
|
||||
particular business request and it delegates any persistence related
|
||||
activities to objects that implement these data access interfaces. These
|
||||
objects are commonly referred to as DAOs (Data Access Objects) and the
|
||||
architectural layer as a DAL (Data Access Layer).</para>
|
||||
|
||||
<para>The benefits of using DAOs in your application are increased
|
||||
portability across persistence technology and ease of testing. Testing is
|
||||
more easily facilitated because a mock or stub implementation of the data
|
||||
access interface can be easily created in a NUnit test so that service
|
||||
layer functionality can be tested without any dependency on the database.
|
||||
This is beneficial because tests that rely on the database are usually
|
||||
hard to set up and tear down and also are impractical for testing
|
||||
exceptional behavior.</para>
|
||||
|
||||
<para>The Data Access Object (DAO) support in Spring is aimed at making it
|
||||
easy to work with data access technologies like ADO.NET and NHibernate in
|
||||
a standardized way. Spring provides two central pieces of functionality to
|
||||
meet this goal. The first is providing a common exception hierarchy across
|
||||
providers and the second is providing base DAOs classes that raise the
|
||||
level of abstraction when performing common ADO.NET operations. This
|
||||
allows one to switch between the aforementioned persistence technologies
|
||||
fairly easily and it also allows one to code without worrying about
|
||||
catching exceptions that are specific to each technology.</para>
|
||||
</section>
|
||||
|
||||
<section id="dao-exceptions">
|
||||
<title>Consistent exception hierarchy</title>
|
||||
|
||||
<para>Database exceptions in the ADO.NET API are not consistent across
|
||||
providers. The .NET 1.1 BCL did not provide a common base class for
|
||||
ADO.NET exceptions. As such you were required to handle exceptions
|
||||
specific to each provider such as
|
||||
<classname>System.Data.SqlClient.SqlException</classname> or
|
||||
<classname>System.Data.OracleClient.OracleException</classname>. The .NET
|
||||
2.0 BCL improved in this regard by introducing a common base class for
|
||||
exceptions, <classname>System.Data.Common.DbException</classname>. However
|
||||
the common DbException is not very portable either as it provides a vendor
|
||||
specific error code as the underlying piece of information as to what went
|
||||
wrong. This error code is different across providers for the same
|
||||
conceptual error, such as a violation of data integrity or providing bad
|
||||
SQL grammar.</para>
|
||||
|
||||
<para>To promote writing portable and descriptive exception handling code
|
||||
Spring provides a convenient translation from technology specific
|
||||
exceptions like <classname>System.Data.SqlClient.SqlException</classname>
|
||||
or <classname>System.Data.OracleClient.OracleException</classname> to its
|
||||
own exception hierarchy with the
|
||||
<classname>Spring.Dao.DataAccessException</classname> as the root
|
||||
exception. These exceptions wrap the original exception so there is never
|
||||
any risk that one might lose any information as to what might have gone
|
||||
wrong.</para>
|
||||
|
||||
<para>In addition to exceptions from ADO.NET providers, Spring can also
|
||||
wrap NHibernate-specific exceptions.. This allows one to handle most
|
||||
persistence exceptions, which are non-recoverable, only in the appropriate
|
||||
layers, without boilerplate using or catch and throw blocks, and exception
|
||||
declarations. As mentioned above, ADO.NET exceptions (including
|
||||
database-specific dialects) are also converted to the same hierarchy,
|
||||
meaning that one can perform some operations with ADO.NET within a
|
||||
consistent programming model. The above holds true for the various
|
||||
template-based versions of the ORM access framework.</para>
|
||||
|
||||
<para>The exception hierarchy that Spring uses is outlined in the
|
||||
following image:</para>
|
||||
|
||||
<mediaobject>
|
||||
<imageobject>
|
||||
<imagedata fileref="images/DataAccessException.gif" />
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
|
||||
<para>(Please note that the class hierarchy detailed in the above image
|
||||
shows only a subset of the whole, rich,
|
||||
<classname>DataAccessException</classname> hierarchy.)</para>
|
||||
|
||||
<para>The exception translation functionality is in the namespace
|
||||
Spring.Data.Support and is based on the interface
|
||||
<literal>IAdoExceptionTranslator</literal> shown below.</para>
|
||||
|
||||
<programlisting>public interface IAdoExceptionTranslator
|
||||
{
|
||||
DataAccessException Translate( string task, string sql, Exception exception );
|
||||
}</programlisting>
|
||||
|
||||
<para>The arguments to the translator are a task string providing a
|
||||
description of the task being attempted, the SQL query or update that
|
||||
caused the problem, and the 'raw' exception thrown by the ADO.NET data
|
||||
provider. The additional task and SQL arguments allow for very readable
|
||||
and clear error messages to be created when an exception occurs.</para>
|
||||
|
||||
<para>A default implementation,
|
||||
<literal>ErrorCodeExceptionTranslator</literal>, is provided that uses the
|
||||
error codes defined for each data provider in the file dbproviders.xml.
|
||||
Refer to this file, an embedded resource in the Spring.Data assembly, for
|
||||
the exact mappings of error codes to Spring DataAccessExceptions.</para>
|
||||
|
||||
<para>A common need is to modify the error codes that are map onto the
|
||||
exception hierarchy. There are several ways to accomplish this
|
||||
task.</para>
|
||||
|
||||
<para>One approach is to override the error codes that are defined in
|
||||
<code>assembly://Spring.Data/Spring.Data.Common/dbproviders.xml</code>. By
|
||||
default, the <link
|
||||
linkend="dbprovider-dbprovider">DbProviderFactory</link> will look for
|
||||
additional metadata for the IoC container it uses internally to define and
|
||||
manage the DbProviders in a file named <literal>dbProviders.xml</literal>
|
||||
located in the root runtime directory. (You can change this location, see
|
||||
the documentation on <link lang=""
|
||||
linkend="dbprovider-dbprovider">DbProvider</link> for more information.)
|
||||
This is a standard Spring application context so all features, such as
|
||||
<link
|
||||
linkend="objects-factory-customizing-factory-postprocessors">ObjectFactoryPostProcessors</link>
|
||||
are available and will be automatically applied. Defining a <link
|
||||
linkend="objects-factory-overrideconfigurer">PropertyOverrideConfigurer</link>
|
||||
in this additional configuration file will allow for you to override
|
||||
specific property values defined in the embedded resource file. As an
|
||||
example, the additional <literal>dbProviders.xml</literal> file shown
|
||||
below will add the error code <literal>2601</literal> to the list of error
|
||||
codes that map to a
|
||||
<literal>DataIntegrityViolationException</literal>.</para>
|
||||
|
||||
<para><programlisting><objects xmlns='http://www.springframework.net'>
|
||||
|
||||
<alias name='SqlServer-2.0' alias='SqlServer2005'/>
|
||||
|
||||
<object name="appConfigPropertyOverride" type="Spring.Objects.Factory.Config.PropertyOverrideConfigurer, Spring.Core">
|
||||
<property name="Properties">
|
||||
<name-values>
|
||||
<add key="SqlServer2005.DbMetadata.ErrorCodes.DataIntegrityViolationCodes"
|
||||
value="544,2601,2627,8114,8115"/>
|
||||
</name-values>
|
||||
</property>
|
||||
</object>
|
||||
|
||||
</objects></programlisting>The reason to define the alias is that <link
|
||||
linkend="objects-factory-overrideconfigurer">PropertyOverrideConfigurer</link>
|
||||
assumes a period <literal>(.)</literal> as the separator to pick out the
|
||||
object name but the names of the objects in
|
||||
<literal>dbProviders.xml</literal> have periods in them (i.e.
|
||||
SqlServer-2.0 or System.Data.SqlClient). Creating an alias that has no
|
||||
periods in the name is a workaround.</para>
|
||||
|
||||
<para>Another way to customize the mappings of error codes to exceptions
|
||||
is to subclass <classname>ErrorCodeExceptionTranslator</classname> and
|
||||
override the method, <literal>DataAccessException
|
||||
TranslateException(string task, string sql, string errorCode, Exception
|
||||
exception)</literal>. This will be called before referencing the metadata
|
||||
to perform exception translation. The vendor specific error code provided
|
||||
as a method argument has already been parsed out of the raw ADO.NET
|
||||
exception. If you create your own specific subclass, then you should set
|
||||
the property <literal>ExceptionTranslator</literal> on
|
||||
<literal>AdoTemplate</literal> and
|
||||
<literal>HibernateTemplate/HibernateTransactionManager</literal> to refer
|
||||
to your custom implementation (unless you are using autowiring).</para>
|
||||
|
||||
<para>The third way is to write an implementation of
|
||||
<literal>IAdoExceptionTranslator</literal> and set the property
|
||||
<literal>FallbackTranslator</literal>'on
|
||||
<literal>ErrorCodeExceptionTranslator</literal>. In this case you are
|
||||
responsible for parsing our the vendor specific error code from the raw
|
||||
ADO.NET exception. As with the case of subclassing
|
||||
ErrorCodeExceptionTranslator, you will need to refer to this custom
|
||||
exception translator when using <literal>AdoTemplate</literal> or
|
||||
<literal>HibernateTemplate/HibernateTransactionManager</literal>.</para>
|
||||
|
||||
<para>The ordering of the exception translation processing is as follows.
|
||||
The method TranslateException is called first, then the standard exception
|
||||
translation logic, then the FallbackTranslator.</para>
|
||||
|
||||
<para>Note that you can use this API directly in your own Spring
|
||||
independent data layer. If you are using Spring's ADO.NET abstraction
|
||||
class, <literal>AdoTemplate</literal>, or
|
||||
<literal>HibernateTemplate</literal>, the converted exceptions will be
|
||||
thrown automatically. Somewhere in between these two cases is using
|
||||
Spring's declarative transaction management features in .NET 2.0 with the
|
||||
raw ADO.NET APIs and using <literal>IAdoExceptionTranslator</literal> in
|
||||
your exception handling layer (which might be implemented in AOP using
|
||||
Spring's exception translation aspect).</para>
|
||||
|
||||
<para>Some of the more common data access exceptions are described here.
|
||||
Please refer to the API documentation for more details.</para>
|
||||
|
||||
<table>
|
||||
<title>Common DataAccessExceptions</title>
|
||||
|
||||
<tgroup cols="2">
|
||||
<colspec align="left" />
|
||||
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Exception</entry>
|
||||
|
||||
<entry>Description</entry>
|
||||
</row>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>BadSqlGrammarException</entry>
|
||||
|
||||
<entry>Exception thrown when SQL specified is invalid.</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>DataIntegrityViolationException</entry>
|
||||
|
||||
<entry>Exception thrown when an attempt to insert or update data
|
||||
results in violation of an integrity constraint. For example,
|
||||
inserting a duplicate key.</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>PermissionDeniedDataAccessException</entry>
|
||||
|
||||
<entry>Exception thrown when the underling resource denied a
|
||||
permission to access a specific element, such as a specific
|
||||
database table.</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>DataAccessResourceFailureException</entry>
|
||||
|
||||
<entry>Exception thrown when a resource fails completely, for
|
||||
example, if we can't connect to a database.</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>ConcurrentyFailureException</entry>
|
||||
|
||||
<entry>Exception thrown when a concurrency error occurs.
|
||||
OptimisticLockingFailureException and
|
||||
PessimisticLockingFailureException are subclasses. This is a
|
||||
useful exception to catch and to retry the transaction again. See
|
||||
Spring's <link linkend="retry-aspect">Retry Aspect</link> for an
|
||||
AOP based solution.</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>OptimisticLockingFailureException</entry>
|
||||
|
||||
<entry>Exception thrown when there an optimistic locking failure
|
||||
occurs. The subclass ObjectOptimisticLockingFailureException can
|
||||
be used to examine the Type and the IDof the object that failed
|
||||
the optimistic locking.</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>PessimisticLockingFailure</entry>
|
||||
|
||||
<entry>Exception thrown when a pessimistic locking failure
|
||||
occures. Subclasses of this exception are
|
||||
CannotAcquireLockException, CannotSerializeTransactionException,
|
||||
and DeadlockLoserDataAccessException.</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>CannotAcquireLockException</entry>
|
||||
|
||||
<entry>Exception thrown when a lock can not be acquired, for
|
||||
example during an update, i..e a select for update</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>CannotSerializeTransactionException</entry>
|
||||
|
||||
<entry>Exception thrown when a transaction can not be
|
||||
serialized.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Consistent abstract classes for DAO support</title>
|
||||
|
||||
<para>To make it easier to work with a variety of data access technologies
|
||||
such as ADO.NET, NHibernate, and iBatis.NET in a consistent way, Spring
|
||||
provides a set of abstract DAO classes that one can extend. These abstract
|
||||
classes have methods for providing the data source and any other
|
||||
configuration settings that are specific to the technology one is
|
||||
currently using.</para>
|
||||
|
||||
<para>DAO support classes:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para><classname>AdoDaoSupport</classname> - super class for ADO.NET
|
||||
data access objects. Requires a
|
||||
<interfacename>DbProvider</interfacename> to be provided; in turn,
|
||||
this class provides a <classname>AdoTemplate</classname> instance
|
||||
initialized from the supplied
|
||||
<interfacename>DbProvider</interfacename> to subclasses. See the
|
||||
documentation for <literal>AdoTemplate</literal> for more
|
||||
information.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><classname>HibernateDaoSupport</classname> - super class for
|
||||
NHibernate data access objects. Requires a
|
||||
<interfacename>ISessionFactory</interfacename> to be provided; in
|
||||
turn, this class provides a <classname>HibernateTemplate</classname>
|
||||
instance initialized from the supplied
|
||||
<interfacename>SessionFactory</interfacename> to subclasses. Can
|
||||
alternatively be initialized directly via a
|
||||
<classname>HibernateTemplate</classname>, to reuse the latter's
|
||||
settings like <interfacename>SessionFactory</interfacename>, flush
|
||||
mode, exception translator, etc. This is contained in a download
|
||||
separate from the main Spring.NET distribution.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
</chapter>
|
||||
172
doc/reference/src/data-quickstart.xml
Normal file
@@ -0,0 +1,172 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<chapter id="data-quickstart">
|
||||
<title>Data Access QuickStart</title>
|
||||
|
||||
<section>
|
||||
<title>Introduction</title>
|
||||
|
||||
<para>The data access quick start demonstrates the API usage of
|
||||
AdoTemplate (both generic and non-generic versions) as well as the use of
|
||||
the object based data access classes contained in Spring.Data.Objects. It
|
||||
uses the Northwind database and is located under the directory
|
||||
examples/DataAccessQuickStart.</para>
|
||||
|
||||
<para>The quick start contains pseudo DAO objects and a collection of
|
||||
NUnit tests to exercise them rather than a full blown application. To run
|
||||
the tests from within VS.NET install <ulink
|
||||
url="http://www.testdriven.net/"><link
|
||||
linkend="???">TestDriven.NET</link></ulink>, <ulink
|
||||
url="http://www.jetbrains.com/resharper/">ReSharper</ulink>, or an
|
||||
equivalent . The listing of DAO classes and the parts of Spring.Data that
|
||||
they demonstrate is shown below.</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para><classname>CommandCallbackDao</classname> - Use of the
|
||||
ICommandCallback and CommandCallbackDelegate</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><classname>ResultSetExtractorDao</classname> - Use of
|
||||
IResultSetExtractor and ResultSetExtractorDelegate</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><classname>RowCallbackDao</classname> - Use of IRowCallback and
|
||||
RowCallbackDelegate</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><classname>RowMapperDao</classname> - Use of IRowMapper and
|
||||
RowMapperDelegate</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><classname>QueryForObject</classname> - Use of QueryForObject
|
||||
method.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><classname>StoredProcDao</classname> - Use of
|
||||
Spring.Data.Objects.StoredProcedure</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>The are simple domain objects in the Spring.DataQuickStart.Domain
|
||||
namespace, collections of which are generally returned from the DAO
|
||||
methods.</para>
|
||||
|
||||
<section>
|
||||
<title>Database configuration</title>
|
||||
|
||||
<para>To get started running the 'unit test' you should configure the
|
||||
database connection string. The listing in
|
||||
DataQuickStart.GenericTemplate.ExampleTests.xml is shown below</para>
|
||||
|
||||
<programlisting><objects xmlns="http://www.springframework.net"
|
||||
xmlns:db="http://www.springframework.net/database">
|
||||
|
||||
<db:provider id="dbProvider"
|
||||
provider="SqlServer-1.1"
|
||||
connectionString="Data Source=(local);Database=Northwind;User ID=springqa;Password=springqa;Trusted_Connection=False"/>
|
||||
|
||||
|
||||
<! -- other definitions not shown
|
||||
|
||||
|
||||
</objects></programlisting>
|
||||
|
||||
<para>You should change the value of the provider element to correspond
|
||||
to you database and the connection string as appropriate. Please refer
|
||||
to the documentation on the <link linkend="dbprovider">DbProvider</link>
|
||||
abstraction for details particular to your database configuration. You
|
||||
should also install the Northwind database, which is available for
|
||||
SqlServer 2005 from this <ulink
|
||||
url="http://www.microsoft.com/downloads/details.aspx?FamilyID=06616212-0356-46a0-8da2-eebc53a68034&DisplayLang=en">download
|
||||
location</ulink>. The minimal schema to support other database providers
|
||||
may be supported in the future.</para>
|
||||
|
||||
<section>
|
||||
<title>AdoTemplate Configuration</title>
|
||||
|
||||
<para>The various DAO objects refer to an instance of AdoTemplate
|
||||
which is responsible for performing data access operations. This is
|
||||
declared in ExampleTest.xml as shown below</para>
|
||||
|
||||
<programlisting> <object id="adoTemplate" type="Spring.Data.Generic.AdoTemplate, Spring.Data">
|
||||
<property name="DbProvider" ref="dbProvider"/>
|
||||
<property name="DataReaderWrapperType" value="Spring.Data.Support.NullMappingDataReader, Spring.Data"/>
|
||||
</object>
|
||||
</programlisting>
|
||||
|
||||
<para>The property DbProvider refers to the database configuration you
|
||||
previously defined. Also the property DataReaderWrapper is set to the
|
||||
NullMappingDataReader that ships with Spring. This provides convenient
|
||||
default values for null values returned from the database. To read
|
||||
more about AdoTemplate, refer to the chapter, <link linkend="???">Data
|
||||
access using ADO.NET</link>.</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>CommandCallback</title>
|
||||
|
||||
<para>The code that exercises the use of a CommandCallback is shown
|
||||
below</para>
|
||||
|
||||
<programlisting> [Test]
|
||||
public void CallbackDaoTest()
|
||||
{
|
||||
CommandCallbackDao commandCallbackDao = ctx["commandCallbackDao"] as CommandCallbackDao;
|
||||
int count = commandCallbackDao.FindCountWithPostalCode("1010");
|
||||
Assert.AreEqual(3, count);
|
||||
}</programlisting>
|
||||
|
||||
<para>The configuration of the CommandCallbackDao is shown below</para>
|
||||
|
||||
<programlisting> <object id="commandCallbackDao" type="Spring.DataQuickStart.Dao.GenericTemplate.CommandCallbackDao, Spring.DataQuickStart">
|
||||
<property name="AdoTemplate" ref="adoTemplate"/>
|
||||
</object></programlisting>
|
||||
|
||||
<para>This the minimal configuration required for a DAO object,
|
||||
typically DAO objects in your application will include other
|
||||
configuraiton information, for example properties to specify the maximum
|
||||
size of the result set returned etc. The implementation of the
|
||||
FindCountWithPostalCode is shown below</para>
|
||||
|
||||
<programlisting> public virtual int FindCountWithPostalCodeWithDelegate(string postalCode)
|
||||
{
|
||||
// Using anonymous delegates allows you to easily reference the
|
||||
// surrounding parameters for use with the DbCommand processing.
|
||||
|
||||
return AdoTemplate.Execute<int>(delegate(DbCommand command)
|
||||
{
|
||||
// Do whatever you like with the DbCommand... downcast to get
|
||||
// provider specific funtionality if necesary.
|
||||
|
||||
command.CommandText = cmdText;
|
||||
|
||||
DbParameter p = command.CreateParameter();
|
||||
p.ParameterName = "@PostalCode";
|
||||
p.Value = postalCode;
|
||||
command.Parameters.Add(p);
|
||||
|
||||
return (int)command.ExecuteScalar();
|
||||
|
||||
});
|
||||
|
||||
}</programlisting>
|
||||
|
||||
<para>Anonymous delegates are used to specify the implementation of the
|
||||
callback function that passes in a DbCommand object. You can then use
|
||||
the DbCommand object as you see fit to access the database. If you are
|
||||
using Spring's delcarative transaction management features then this
|
||||
DbCommand would have its transaction and connection properties based on
|
||||
the context of the surrounding transaction. All resource management for
|
||||
the DbCommand are handled for you by the framework, as well as error
|
||||
reporting on error etc. If you execute the test, it will pass, assuming
|
||||
you haven't modified any data in the Northwind database from its raw
|
||||
installation.</para>
|
||||
</section>
|
||||
</section>
|
||||
</chapter>
|
||||
464
doc/reference/src/dbprovider.xml
Normal file
@@ -0,0 +1,464 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<chapter id="dbprovider">
|
||||
<title>DbProvider</title>
|
||||
|
||||
<section id="dbprovider-introduction">
|
||||
<title>Introduction</title>
|
||||
|
||||
<para>Spring provides a generic factory for creating ADO.NET API artifacts
|
||||
such as <code><classname>IDbConnection</classname></code> and
|
||||
<code><classname>IDbCommand</classname></code>. The factory API is very
|
||||
similar to the one introduced in .NET 2.0 but adds extra metadata needed
|
||||
by Spring to support features provided by its DAO/ADO.NET framework such
|
||||
as error code translation to a DAO exception hierarchy. The factory itself
|
||||
is configured by using a standard Spring XML based configuration file
|
||||
though it is unlikely you will need to modify those settings yourself, you
|
||||
only need be concerned with using the factory. Out of the box several
|
||||
popular databases are supported and an extension mechanism is available
|
||||
for defining new database providers or modifying existing ones. A custom
|
||||
database namespace for configuration aids in making terse XML based
|
||||
declarations of Spring's database objects you wish to use.</para>
|
||||
|
||||
<para>The downside of Spring's factory as compared to the one in .NET 2.0
|
||||
is that the types returned are lower level interfaces and not the abstract
|
||||
base classes in System.Data.Common. However, there are still 'holes' in
|
||||
the current .NET 2.0 provider classes that are 'plugged' with Spring's
|
||||
provider implementation. One of the most prominent is the that the top
|
||||
level DbException exposes the HRESULT of the remote procedure call, which
|
||||
is not what you are commonly looking for when things go wrong. As such
|
||||
Spring's provider factory exposes the vendor sql error code and also maps
|
||||
that error code onto a consistent data access exception hierarchy. This
|
||||
makes writing portable exception handlers much easier. In addition, the
|
||||
DbParameter class doesn't provide the most common convenient methods you
|
||||
would expect as when using say the SqlServer provider. If you need to
|
||||
access the BCL provider abstraction, you still can through Spring's
|
||||
provider class. Furthermore, a small wrapper around the standard BCL
|
||||
provider abstraction allows for integration with Spring's transaction
|
||||
management facilities, allowing you to create a DbCommand with its
|
||||
connection and transaction properties already set based on the transaction
|
||||
calling context.</para>
|
||||
</section>
|
||||
|
||||
<section id="dbprovider-dbprovider">
|
||||
<title>IDbProvider and DbProviderFactory</title>
|
||||
|
||||
<para>The <code><interfacename>IDbProvider</interfacename></code> API is
|
||||
shown below and should look familiar to anyone using .NET 2.0 data
|
||||
providers. Note that Spring's DbProvider abstraction can be used on .NET
|
||||
1.1 in addition to .NET 2.0</para>
|
||||
|
||||
<programlisting> public interface IDbProvider
|
||||
{
|
||||
IDbCommand CreateCommand();
|
||||
|
||||
object CreateCommandBuilder();
|
||||
|
||||
IDbConnection CreateConnection();
|
||||
|
||||
IDbDataAdapter CreateDataAdapter();
|
||||
|
||||
IDbDataParameter CreateParameter();
|
||||
|
||||
string CreateParameterName(string name);
|
||||
|
||||
string CreateParameterNameForCollection(string name);
|
||||
|
||||
IDbMetadata DbMetadata
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
string ConnectionString
|
||||
{
|
||||
set;
|
||||
get;
|
||||
}
|
||||
|
||||
string ExtractError(Exception e);
|
||||
|
||||
bool IsDataAccessException(Exception e);
|
||||
|
||||
}</programlisting>
|
||||
|
||||
<para>ExtractError is used to return an error string for translation into
|
||||
a DAO exception. On .NET 1.1 the method IsDataAccessException is used to
|
||||
determine if the thrown exception is related to data access since in .NET
|
||||
1.1 there isn't a common base class for database exceptions.
|
||||
CreateParameterName is used to create the string for parameters used in a
|
||||
CommandText object while CreateParameterNameForCollection is used to
|
||||
create the string for a IDataParameter.ParameterName, typically contained
|
||||
inside a IDataParameterCollection.</para>
|
||||
|
||||
<para>The class <classname>DbProviderFactory</classname> creates
|
||||
IDbProvider instances given a provider name. The connection string
|
||||
property will be used to set the IDbConnection returned by the factory if
|
||||
present. The provider names, and corresponding database, currently
|
||||
configured are listed below.</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para><code>SqlServer-1.1</code> - Microsoft SQL Server, provider
|
||||
V1.0.5000.0 in framework .NET V1.1</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><code>SqlServer-2.0</code> (aliased to
|
||||
<code>System.Data.SqlClient</code>) - Microsoft SQL Server, provider
|
||||
V2.0.0.0 in framework .NET V2.0</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>SqlServerCe-3.1</literal> (aliased to
|
||||
<literal>System.Data.SqlServerCe</literal>) - Microsoft SQL Server
|
||||
Compact Edition, provider V9.0.242.0</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><code>OleDb-1.1</code> - OleDb, provider V1.0.5000.0 in
|
||||
framework .NET V1.1</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><code>OleDb-2.0</code> (aliased to
|
||||
<code>System.Data.OleDb</code>) - OleDb, provider V2.0.0.0 in
|
||||
framework .NET V2.0</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><code>OracleClient-2.0</code> (aliased to
|
||||
<code>System.Data.OracleClient</code>) - Oracle, Microsoft provider
|
||||
V2.0.0.0</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><code>OracleODP-2.0</code> (aliased to
|
||||
<code>System.DataAccess.Client</code>) - Oracle, Oracle provider
|
||||
V2.102.2.20</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><code>MySql</code> - MySQL, MySQL provider 1.0.10.1</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>MySql-1.0.9</literal> - MySQL, MySQL provider
|
||||
1.0.9</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>MySql-5.0</literal> - MySQL, MySQL provider
|
||||
5.0.7.0</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>MySql-5.0.8.1</literal> - MySQL, MySQL provider
|
||||
5.0.8.1</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>MySql-5.1 </literal>- MySQL, MySQL provider
|
||||
5.1.2.2</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>MySql-5.1.4</literal> - (aliased to
|
||||
<literal>MySql.Data.MySqlClient</literal>) MySQL, MySQL provider
|
||||
5.1.2.2</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>Npgsql-1.0</literal> - Postgresql provider 1.0.0.0 (and
|
||||
1.0.0.1 - were build with same version info)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>Npgsql-2.0-beta1</literal> - Postgresql provider
|
||||
1.98.1.0 beta 1</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>DB2-9.0.0-1.1</literal> - IBM DB2 Data Provider 9.0.0
|
||||
for .NET Framework 1.1</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>DB2-9.0.0-2.0 </literal>- (aliased to
|
||||
<literal>IBM.Data.DB2</literal>) - IBM DB2 Data Provider 9.0.0 for
|
||||
.NET Framework 2.0</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>DB2-9.1.0-1.1</literal> - IBM DB2 Data Provider 9.1.0
|
||||
for .NET Framework 1.1</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>DB2-9.1.0.2</literal> - (aliased to
|
||||
<literal>IBM.Data.DB2.9.1.0</literal>) - IBM DB2 Data Provider 9.1.0
|
||||
for .NET Framework 2.0</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>SQLite-1.0.43 </literal>SQLite provider 1.0.43 for .NET
|
||||
Framework 2.0</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>SQLite-1.0.47 </literal>- (aliased to
|
||||
System.Data.SQLite) - SQLite provider 1.0.43 for .NET Framework
|
||||
2.0</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>SybaseAse-12</literal> - Sybase ASE provider for ASE
|
||||
12.x</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>SybaseAse-15</literal> - Sybase ASE provider for ASE
|
||||
15.x</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>Odbc-1.1</literal> - ODBC provider V1.0.5000.0 in
|
||||
framework .NET V1.1</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><literal>Odbc-2.0</literal> - ODBC provider V2.0.0.0 in
|
||||
framework .NET V2</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>An example using DbProviderFactory is shown below</para>
|
||||
|
||||
<programlisting>IDbProvider dbProvider = DbProviderFactory.GetDbProvider("System.Data.SqlClient");</programlisting>
|
||||
|
||||
<para>The default definitions of the providers are contained in the
|
||||
assembly resource
|
||||
<code>assembly://Spring.Data/Spring.Data.Common/dbproviders.xml</code>.
|
||||
Future additions to round out the database coverage are forthcoming. The
|
||||
current crude mechanism to add additional providers, or to apply any
|
||||
standard Spring <interfacename>IApplicationContext</interfacename>
|
||||
functionality, such as applying AOP advice, is to set the public static
|
||||
property DBPROVIDER_ADDITIONAL_RESOURCE_NAME in
|
||||
<classname>DbProviderFactory</classname> to a Spring resource location.
|
||||
The default value is <code>file://dbProviders.xml</code>. (That isn't a
|
||||
typo, there is a difference in case with the name of the embedded
|
||||
resource). This crude mechanism will eventually be replaced with one based
|
||||
on a custom configuration section in App.config/Web.config.</para>
|
||||
|
||||
<para>It may happen that the version number of an assembly you have
|
||||
downloaded is different than the one listed above. If it is a point
|
||||
release, i.e. the API hasn't changed in anyway that is material to your
|
||||
application, you should add an assembly redirect of the form shown
|
||||
below.</para>
|
||||
|
||||
<programlisting><dependentAssembly>
|
||||
<assemblyIdentity name="MySql.Data"
|
||||
publicKeyToken="c5687fc88969c44d"
|
||||
culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535"
|
||||
newVersion="1.0.10.1"/>
|
||||
</dependentAssembly></programlisting>
|
||||
|
||||
<para>This redirects any reference to an older version of the assembly
|
||||
MySql.Data to the version 1.0.10.1.</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>XML based configuration</title>
|
||||
|
||||
<para>Creating a DbProvider in Spring's XML configuration file is shown
|
||||
below in the typical case of using it to specify the DbProvider property
|
||||
on an AdoTemplate.</para>
|
||||
|
||||
<programlisting><objects xmlns='http://www.springframework.net'
|
||||
xmlns:db="http://www.springframework.net/database">
|
||||
|
||||
<db:provider id="DbProvider"
|
||||
provider="System.Data.SqlClient"
|
||||
connectionString="Data Source=(local);Database=Spring;User ID=springqa;Password=springqa;Trusted_Connection=False"/>
|
||||
|
||||
<object id="adoTemplate" type="Spring.Data.AdoTemplate, Spring.Data">
|
||||
<property name="DbProvider" ref="DbProvider"/>
|
||||
</object>
|
||||
|
||||
</objects></programlisting>
|
||||
|
||||
<para>A custom namespace should be registered in the main application
|
||||
configuration file to use this syntax. This configuration, only for the
|
||||
parsers, is shown below. Additional section handlers are needed to specify
|
||||
the rest of the Spring configuration locations as described in previous
|
||||
chapters.</para>
|
||||
|
||||
<programlisting><configuration>
|
||||
|
||||
<configSections>
|
||||
<sectionGroup name="spring">
|
||||
<section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core" />
|
||||
</sectionGroup>
|
||||
</configSections>
|
||||
|
||||
<spring>
|
||||
<parsers>
|
||||
<parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />
|
||||
</parsers>
|
||||
</spring>
|
||||
|
||||
</configuration></programlisting>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Connection String management</title>
|
||||
|
||||
<para>There are a few options available to help manage your connection
|
||||
strings.</para>
|
||||
|
||||
<para>The first option is to leverage the Spring property replacement
|
||||
functionality, as described in <xref
|
||||
linkend="objects-factory-placeholderconfigurer" />. This lets you insert
|
||||
variable names as placeholders for values in a Spring configuration file.
|
||||
In the following example specific parts of a connection string have been
|
||||
parameterized but you can also use a variable to set the entire connection
|
||||
string.</para>
|
||||
|
||||
<para>An example of such a setting is shown below</para>
|
||||
|
||||
<programlisting><configuration>
|
||||
<configSections>
|
||||
<sectionGroup name="spring">
|
||||
<section name='context' type='Spring.Context.Support.ContextHandler, Spring.Core'/>
|
||||
</sectionGroup>
|
||||
|
||||
<section name="databaseSettings" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
|
||||
</configSections>
|
||||
|
||||
<spring>
|
||||
<context>
|
||||
<resource uri="Aspects.xml" />
|
||||
<resource uri="Services.xml" />
|
||||
<resource uri="Dao.xml" />
|
||||
</context>
|
||||
</spring>
|
||||
|
||||
<!-- These properties are referenced in Dao.xml -->
|
||||
<databaseSettings>
|
||||
<add key="db.datasource" value="(local)" />
|
||||
<add key="db.user" value="springqa" />
|
||||
<add key="db.password" value="springqa" />
|
||||
<add key="db.database" value="Northwind" />
|
||||
</databaseSettings>
|
||||
|
||||
|
||||
</configuration></programlisting>
|
||||
|
||||
<para>Where <literal>Dao.xml</literal> has a connection string as shown
|
||||
below</para>
|
||||
|
||||
<programlisting><objects xmlns='http://www.springframework.net'
|
||||
xmlns:db="http://www.springframework.net/database">
|
||||
|
||||
<db:provider id="DbProvider"
|
||||
provider="System.Data.SqlClient"
|
||||
connectionString="${db.datasource};Database=${db.database};User ID=${db.user};Password=${db.password};Trusted_Connection=False"/>
|
||||
|
||||
<object id="adoTemplate" type="Spring.Data.AdoTemplate, Spring.Data">
|
||||
<property name="DbProvider" ref="DbProvider"/>
|
||||
</object>
|
||||
|
||||
<!-- configuration of what values to substitute for ${ } variables listed above -->
|
||||
<object name="appConfigPropertyHolder"
|
||||
type="Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer, Spring.Core">
|
||||
<property name="configSections" value="DatabaseConfiguration"/>
|
||||
</object>
|
||||
|
||||
</objects></programlisting>
|
||||
|
||||
<para>Please refer to the Section <xref
|
||||
linkend="objects-factory-placeholderconfigurer" /> for more
|
||||
information.</para>
|
||||
</section>
|
||||
|
||||
<section id="dbprovider-additional">
|
||||
<title>Additional IDbProvider implementations</title>
|
||||
|
||||
<para>Spring provides some convenient implementations of the IDbProvider
|
||||
interface that add addtional behavior on top of the standard
|
||||
implementation.</para>
|
||||
|
||||
<section id="dbprovider-usercredentials">
|
||||
<title>UserCredentialsDbProvider</title>
|
||||
|
||||
<para>This <classname>UserCredentialsDbProvider</classname> will allow
|
||||
you to change the username and password of a database connection at
|
||||
runtime. The API contains the properties <literal>Username</literal> and
|
||||
<literal>Password</literal> which are used as the default strings
|
||||
representing the user and password in the connection string. You can
|
||||
then change the value of these properties in the connection string by
|
||||
calling the method <literal>SetCredentialsForCurrentThread</literal> and
|
||||
fall back to the default values by calling the method
|
||||
<literal>RemoveCredentialsFromCurrentThread</literal>. You call the
|
||||
<literal>SetCredentialsForCurrentThread</literal> method at runtime,
|
||||
before any data access occurs, to determine which database user should
|
||||
be used for the current user-case. Which user to select is up to you.
|
||||
You may retrieve the user information from an HTTP session for example.
|
||||
Example configuration and usage is shown below</para>
|
||||
|
||||
<programlisting><object id="DbProvider" type="Spring.Data.Common.UserCredentialsDbProvider, Spring.Data">
|
||||
<property name="TargetDbProvider" ref="targetDbProvider"/>
|
||||
<property name="Username" value="User ID=defaultName"/>
|
||||
<property name="Password" value="Password=defaultPass"/>
|
||||
</object>
|
||||
|
||||
<db:provider id="targetDbProvider" provider="SqlServer-2.0"
|
||||
connectionString="Data Source=MARKT60\SQL2005;Database=Spring;Trusted_Connection=False"/></programlisting>
|
||||
|
||||
<para>If you use dependency injection to configure a class with a
|
||||
property of the type <literal>IDbProvider</literal>, you will need to
|
||||
downcast to the subtype or you can change your class to have a property
|
||||
of the type <literal>UserCredentialsDbProvider</literal> instead of
|
||||
<literal>IDbProvider</literal>.</para>
|
||||
|
||||
<programlisting>userCredentialsDbProvider.SetCredentialsForCurrentThread("User ID=springqa", "Password=springqa");</programlisting>
|
||||
|
||||
<para><literal>UserCredentialsDbProvider's</literal> has a base class,
|
||||
<literal>DelegatingDbProvider</literal>, and is intended for you to use
|
||||
in your own implementations that delegate calls to a target
|
||||
<literal>IDbProvider</literal> instance. This class in meant to be
|
||||
subclassed with subclasses overriding only those methods, such as
|
||||
<literal>CreateConnection()</literal>, that should not simply delegate
|
||||
to the target <literal>IDbProvider</literal>.</para>
|
||||
</section>
|
||||
|
||||
<section id="dbprovider-multidelegating">
|
||||
<title>MultiDelegatingDbProvider</title>
|
||||
|
||||
<para>There are use-cases in which there will need to be a runtime
|
||||
selection of the database to connect to among many possible candidates.
|
||||
This is often the case where the same schema is installed in separate
|
||||
databases for different clients. The
|
||||
<classname>MultiDelegatingDbProvider</classname> implements the
|
||||
<classname>IDbProvider</classname> interface and provides an abstraction
|
||||
to the multiple databases and can be used in DAO layer such that the DAO
|
||||
layer is unaware of the switching between databases.
|
||||
<classname>MultiDelegatingDbProvider</classname> does its job by looking
|
||||
into thread local storage under the key dbProviderName. This storage
|
||||
location stores the name of the dbProvider that is to be used for
|
||||
processing the request. <classname>MultiDelegatingDbProvider</classname>
|
||||
is configured using the dictionary property
|
||||
<literal>TargetDbProviders</literal>. The key of this dictionary
|
||||
contains the name of a dbProvider and its value is a dbProvider object.
|
||||
(You can also provide this dictionary as a constructor argument.) During
|
||||
request processing, once you have determined which target dbProvider
|
||||
should be use, in this example database1ProviderName, you should execute
|
||||
the following code
|
||||
<literal>LogicalThreadContext.SetData("dbProviderName",
|
||||
"database1ProviderName")</literal> and then call the data access
|
||||
layer.</para>
|
||||
|
||||
<para></para>
|
||||
</section>
|
||||
</section>
|
||||
</chapter>
|
||||
990
doc/reference/src/expressions.xml
Normal file
@@ -0,0 +1,990 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<chapter id="expressions">
|
||||
<title>Expression Evaluation</title>
|
||||
<sect1 id="expressions-introduction">
|
||||
<title>Introduction</title>
|
||||
|
||||
<para>The Spring.Expressions namespace provides a powerful expression
|
||||
language for querying and manipulating an object graph at runtime. The
|
||||
language supports setting and getting of property values, property
|
||||
assignment, method invocation, accessing the context of arrays,
|
||||
collections and indexers, logical and arithmetic operators, named
|
||||
variables, and retrieval of objects by name from Spring's IoC container.
|
||||
It also supports list projection and selection, as well as common list
|
||||
aggregators.</para>
|
||||
|
||||
<para>The functionality provided in this namespace serves as the
|
||||
foundation for a variety of other features in Spring.NET such as enhanced
|
||||
property evaluation in the XML based configuration of the IoC container, a
|
||||
Data Validation framework, and a Data Binding framework for ASP.NET. You
|
||||
will likely find other cool uses for this library in your own work where
|
||||
run-time evaluation of criteria based on an object's state is required.
|
||||
For those with a Java background, the Spring.Expressions namespace
|
||||
provides functionality similar to the Java based Object Graph Navigation
|
||||
Language, <ulink url="http://www.ognl.org/">OGNL</ulink>.</para>
|
||||
|
||||
<para>This chapter covers the features of the expression language using an
|
||||
Inventor and Inventor's Society class as the target objects for expression
|
||||
evaluation. The class declarations and the data used to populate them are
|
||||
listed at the end of the chapter in section <xref
|
||||
linkend="expressions-classes" />. These classes are blatantly taken from
|
||||
the NUnit tests for the Expressions namespace which you can refer to for
|
||||
additional example usage.</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="expressions-evaluating">
|
||||
<title>Evaluating Expressions</title>
|
||||
|
||||
<para>The simplest, but not the most efficient way to perform expression
|
||||
evaluation is by using one of the static convenience methods of the
|
||||
<classname>ExpressionEvaluator</classname> class:<programlisting>public static object GetValue(object root, string expression);
|
||||
|
||||
public static object GetValue(object root, string expression, IDictionary variables)
|
||||
|
||||
public static void SetValue(object root, string expression, object newValue)
|
||||
|
||||
public static void SetValue(object root, string expression, IDictionary variables, object newValue)</programlisting>
|
||||
The first argument is the 'root' object that the expression string (2nd argument) will be
|
||||
evaluated against. The third argument is used to support variables in the expression
|
||||
and will be discussed later.
|
||||
|
||||
Simple usage to get the value of an object property is shown below using the
|
||||
<classname>Inventor</classname> class.
|
||||
You can find the class listing in section <xref linkend="expressions-classes"/>.
|
||||
<programlisting>Inventor tesla = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian");
|
||||
|
||||
tesla.PlaceOfBirth.City = "Smiljan";
|
||||
|
||||
string evaluatedName = (string) ExpressionEvaluator.GetValue(tesla, "Name");
|
||||
|
||||
string evaluatedCity = (string) ExpressionEvaluator.GetValue(tesla, "PlaceOfBirth.City"));</programlisting>
|
||||
The value of 'evaluatedName' is 'Nikola Tesla' and that of 'evaluatedCity'
|
||||
is 'Smiljan'. A period is used to navigate the nested properties of the
|
||||
object. Similarly to set the property of an object, say we want to rewrite
|
||||
history and change Tesla's city of birth, we would simply add the
|
||||
following line <programlisting>ExpressionEvaluator.SetValue(tesla, "PlaceOfBirth.City", "Novi Sad");</programlisting></para>
|
||||
|
||||
<para>A much better way to evaluate expressions is to parse them once and
|
||||
then evaluate as many times as you want
|
||||
using<classname>Expression</classname>class. Unlike
|
||||
<classname>ExpressionEvaluator</classname>, which parses expression every
|
||||
time you invoke one of its methods, <classname>Expression</classname>
|
||||
class will cache the parsed expression for increased performance. The
|
||||
methods of this class are listed below: <programlisting>public static IExpression Parse(string expression)
|
||||
|
||||
public override object Get(object context, IDictionary variables)
|
||||
|
||||
public override void Set(object context, IDictionary variables, object newValue)</programlisting>
|
||||
The retrieval of the Name property in the previous example using the
|
||||
Expression class is shown below <programlisting>IExpression exp = Expression.Parse("Name");
|
||||
|
||||
string evaluatedName = (string) exp.GetValue(tesla, null);</programlisting></para>
|
||||
|
||||
<para>The difference in performance between the two approaches, when
|
||||
evaluating the same expression many times, is several orders of magnitude,
|
||||
so you should only use convenience methods of the
|
||||
<classname>ExpressionEvaluator</classname> class when you are doing
|
||||
one-off expression evaluations. In all other cases you should parse the
|
||||
expression first and then evaluate it as many times as you need.</para>
|
||||
|
||||
<para>There are a few exception classes to be aware of when using the
|
||||
<classname>ExpressionEvaluator</classname>. These are
|
||||
<classname>InvalidPropertyException</classname>, when you refer to a
|
||||
property that doesn't exist,
|
||||
<classname>NullValueInNestedPathException</classname>, when a null value
|
||||
is encountered when traversing through the nested property list, and
|
||||
<classname>ArgumentException</classname> and
|
||||
<classname>NotSupportedException</classname> when you pass in values that
|
||||
are in error in some other manner.</para>
|
||||
|
||||
<para>The expression language is based on a grammar and uses <ulink
|
||||
url="http://www.antlr.org/">ANTLR</ulink> to construct the lexer and
|
||||
parser. Errors relating to bad syntax of the language will be caught at
|
||||
this level of the language implementation. For those interested in the
|
||||
digging deeper into the implementation, the grammar file is named
|
||||
Expression.g and is located in the src directory of the namespace. As a
|
||||
side note, the release version of the ANTLR DLL included with Spring.NET
|
||||
was signed with the Spring.NET key, which means that you should always use
|
||||
the included version of <literal>antlr.runtime.dll</literal> within your
|
||||
application. Upcoming releases of ANTLR will provide strongly signed
|
||||
assemblies, which will remove this requirement.</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="expressions-language-ref">
|
||||
<title>Language Reference</title>
|
||||
|
||||
<sect2 id="expressions-literals">
|
||||
<title>Literal expressions</title>
|
||||
|
||||
<para>The types of literal expressions supported are strings, dates,
|
||||
numeric values (int, real, and hex), boolean and null. String are
|
||||
delimited by single quotes. To put a single quote itself in a string use
|
||||
the backslash character. The following listing shows simple usage of
|
||||
literals. Typically they would not be used in isolation like this, but
|
||||
as part of a more complex expression, for example using a literal on one
|
||||
side of a logical comparison operator. <programlisting>string helloWorld = (string) ExpressionEvaluator.GetValue(null, "'Hello World'"); // evals to "Hello World"
|
||||
|
||||
string tonyPizza = (string) ExpressionEvaluator.GetValue(null, "'Tony\\'s Pizza'"); // evals to "Tony's Pizza"
|
||||
|
||||
double avogadrosNumber = (double) ExpressionEvaluator.GetValue(null, "6.0221415E+23");
|
||||
|
||||
int maxValue = (int) ExpressionEvaluator.GetValue(null, "0x7FFFFFFF"); // evals to 2147483647
|
||||
|
||||
DateTime birthday = (DateTime) ExpressionEvaluator.GetValue(null, "date('1974/08/24')");
|
||||
|
||||
DateTime exactBirthday =
|
||||
(DateTime) ExpressionEvaluator.GetValue(null, " date('19740824T131030', 'yyyyMMddTHHmmss')");
|
||||
|
||||
bool trueValue = (bool) ExpressionEvaluator.GetValue(null, "true");
|
||||
|
||||
object nullValue = ExpressionEvaluator.GetValue(null, "null");</programlisting>
|
||||
Note that the extra backslash character in Tony's Pizza is to satisfy C#
|
||||
escape syntax. Numbers support the use of the negative sign, exponential
|
||||
notation, and decimal points. By default real numbers are parsed using
|
||||
<classname>Double.Parse</classname> unless the format character "M" or
|
||||
"F" is supplied, in which case <classname>Decimal.Parse</classname> and
|
||||
<classname>Single.Parse</classname> would be used respectfully. As shown
|
||||
above, if two arguments are given to the date literal then
|
||||
<classname>DateTime.ParseExact</classname> will be used. Note that all
|
||||
parse methods of classes that are used internally reference the
|
||||
<classname>CultureInfo.InvariantCulture</classname>.</para>
|
||||
</sect2>
|
||||
|
||||
<!-- PROPERTIES -->
|
||||
|
||||
<sect2 id="expressions-properties">
|
||||
<title>Properties, Arrays, Lists, Dictionaries, Indexers</title>
|
||||
|
||||
<para>As shown in the previous example in <xref
|
||||
linkend="expressions-evaluating" />, navigating through properties is
|
||||
easy, just use a period to indicate a nested property value. The
|
||||
instances of <classname>Inventor</classname> class,
|
||||
<emphasis>pupin</emphasis> and <emphasis>tesla</emphasis>, were
|
||||
populated with data listed in section <xref
|
||||
linkend="expressions-classes" />. To navigate "down" and get Tesla's
|
||||
year of birth and Pupin's city of birth the following expressions are
|
||||
used <programlisting>int year = (int) ExpressionEvaluator.GetValue(tesla, "DOB.Year")); // 1856
|
||||
|
||||
string city = (string) ExpressionEvaluator.GetValue(pupin, "PlaCeOfBirTh.CiTy"); // "Idvor"</programlisting>
|
||||
For the sharp-eyed, that isn't a typo in the property name for place of
|
||||
birth. The expression uses mixed cases to demonstrate that the
|
||||
evaluation is case insensitive.</para>
|
||||
|
||||
<para>The contents of arrays and lists are obtained using square bracket
|
||||
notation. <programlisting>// Inventions Array
|
||||
string invention = (string) ExpressionEvaluator.GetValue(tesla, "Inventions[3]"); // "Induction motor"
|
||||
|
||||
// Members List
|
||||
string name = (string) ExpressionEvaluator.GetValue(ieee, "Members[0].Name"); // "Nikola Tesla"
|
||||
|
||||
// List and Array navigation
|
||||
string invention = (string) ExpressionEvaluator.GetValue(ieee, "Members[0].Inventions[6]") // "Wireless communication"</programlisting></para>
|
||||
|
||||
<para>The contents of dictionaries are obtained by specifying the
|
||||
literal key value within the brackets. In this case, because keys for
|
||||
the <emphasis>Officers</emphasis> dictionary are strings, we can specify
|
||||
string literal.<programlisting>// Officer's Dictionary
|
||||
Inventor pupin = (Inventor) ExpressionEvaluator.GetValue(ieee, "Officers['president']";
|
||||
|
||||
string city = (string) ExpressionEvaluator.GetValue(ieee, "Officers['president'].PlaceOfBirth.City"); // "Idvor"
|
||||
|
||||
ExpressionEvaluator.SetValue(ieee, "Officers['advisors'][0].PlaceOfBirth.Country", "Croatia");</programlisting></para>
|
||||
|
||||
<para>You may also specify non literal values in place of the quoted
|
||||
literal values by using another expression inside the square brackets
|
||||
such as variable names or static properties/methods on other types.
|
||||
These features are discussed in other sections.</para>
|
||||
|
||||
<para>Indexers are similarly referenced using square brackets. The
|
||||
following is a small example that shows the use of indexers.
|
||||
Multidimensional indexers are also supported. <programlisting>public class Bar
|
||||
{
|
||||
private int[] numbers = new int[] {1, 2, 3};
|
||||
|
||||
public int this[int index]
|
||||
{
|
||||
get { return numbers[index];}
|
||||
set { numbers[index] = value; }
|
||||
}
|
||||
}
|
||||
|
||||
Bar b = new Bar();
|
||||
|
||||
int val = (int) ExpressionEvaluator.GetValue(bar, "[1]") // evaluated to 2
|
||||
|
||||
ExpressionEvaluator.SetValue(bar, "[1]", 3); // set value to 3</programlisting></para>
|
||||
|
||||
<sect3>
|
||||
<title>Defining Arrays, Lists and Dictionaries Inline</title>
|
||||
|
||||
<para>In addition to accessing arrays, lists and dictionaries by
|
||||
navigating the graph for the context object, Spring.NET Expression
|
||||
Language allows you to define them inline, within the expression.
|
||||
Inline lists are defined by simply enclosing a comma separated list of
|
||||
items with curly brackets:<programlisting>{1, 2, 3, 4, 5}
|
||||
{'abc', 'xyz'}</programlisting> If you want to ensure that a strongly typed
|
||||
array is initialized instead of a weakly typed list, you can use array
|
||||
initializer instead: <programlisting>new int[] {1, 2, 3, 4, 5}
|
||||
new string[] {'abc', 'xyz'}</programlisting></para>
|
||||
|
||||
<para>Dictionary definition syntax is a bit different: you need to use
|
||||
a # prefix to tell expression parser to expect key/value pairs within
|
||||
the brackets and to specify a comma separated list of key/value pairs
|
||||
within the brackets:<programlisting>#{'key1' : 'Value 1', 'today' : DateTime.Today}
|
||||
#{1 : 'January', 2 : 'February', 3 : 'March', ...}</programlisting></para>
|
||||
|
||||
<para>Arrays, lists and dictionaries created this way can be used
|
||||
anywhere where arrays, lists and dictionaries obtained from the object
|
||||
graph can be used, which we will see later in the examples.</para>
|
||||
|
||||
<para>Keep in mind that even though examples above use literals as
|
||||
array/list elements and dictionary keys and values, that's only to
|
||||
simplify the examples -- you can use any valid expression wherever
|
||||
literals are used.</para>
|
||||
</sect3>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="expressions-methods">
|
||||
<title>Methods</title>
|
||||
|
||||
<para>Methods are invoked using typical C# programming syntax. You may
|
||||
also invoke methods on literals.</para>
|
||||
|
||||
<programlisting>//string literal
|
||||
char[] chars = (char[]) ExpressionEvaluator.GetValue(null, "'test'.ToCharArray(1, 2)")) // 't','e'
|
||||
|
||||
//date literal
|
||||
int year = (int) ExpressionEvaluator.GetValue(null, "date('1974/08/24').AddYears(31).Year") // 2005
|
||||
|
||||
// object usage, calculate age of tesla navigating from the IEEE society.
|
||||
|
||||
ExpressionEvaluator.GetValue(ieee, "Members[0].GetAge(date('2005-01-01')") // 149 (eww..a big anniversary is coming up ;)</programlisting>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="expressions-operators">
|
||||
<title>Operators</title>
|
||||
|
||||
<sect3 id="expressions-relational">
|
||||
<title>Relational operators</title>
|
||||
|
||||
<para>The relational operators; equal, not equal, less than, less than
|
||||
or equal, greater than, and greater than or equal are supported using
|
||||
standard operator notation. These operators take into account if the
|
||||
object implements the <classname>IComparable</classname> interface.
|
||||
Enumerations are also supported but you will need to register the
|
||||
enumeration type, as described in Section <xref
|
||||
linkend="expressions-typeregistration" />, in order to use an
|
||||
enumeration value in an expression if it is not contained in the
|
||||
mscorlib.</para>
|
||||
|
||||
<programlisting>ExpressionEvaluator.GetValue(null, "2 == 2") // true
|
||||
|
||||
ExpressionEvaluator.GetValue(null, "date('1974-08-24') != DateTime.Today") // true
|
||||
|
||||
ExpressionEvaluator.GetValue(null, "2 < -5.0") // false
|
||||
|
||||
ExpressionEvaluator.GetValue(null, "DateTime.Today <= date('1974-08-24')") // false
|
||||
|
||||
ExpressionEvaluator.GetValue(null, "'Test' >= 'test'") // true</programlisting>
|
||||
|
||||
<para>Enumerations can be evaluated as shown below <programlisting>FooColor fColor = new FooColor();
|
||||
|
||||
ExpressionEvaluator.SetValue(fColor, "Color", KnownColor.Blue);
|
||||
|
||||
bool trueValue = (bool) ExpressionEvaluator.GetValue(fColor, "Color == KnownColor.Blue"); //true</programlisting>
|
||||
Where FooColor is the following class. <programlisting>public class FooColor
|
||||
{
|
||||
private KnownColor knownColor;
|
||||
|
||||
public KnownColor Color
|
||||
{
|
||||
get { return knownColor;}
|
||||
set { knownColor = value; }
|
||||
}
|
||||
}</programlisting></para>
|
||||
|
||||
<para>In addition to standard relational operators, Spring.NET
|
||||
Expression Language supports some additional, very useful operators
|
||||
that were "borrowed" from SQL, such as <emphasis>in</emphasis>,
|
||||
<emphasis>like</emphasis> and <emphasis>between</emphasis>, as well as
|
||||
<emphasis>is</emphasis> and <emphasis>matches</emphasis> operators,
|
||||
which allow you to test if object is of a specific type or if the
|
||||
value matches a regular expression.<programlisting>ExpressionEvaluator.GetValue(null, "3 in {1, 2, 3, 4, 5}") // true
|
||||
|
||||
ExpressionEvaluator.GetValue(null, "'Abc' like '[A-Z]b*'") // true
|
||||
|
||||
ExpressionEvaluator.GetValue(null, "'Abc' like '?'") // false
|
||||
|
||||
ExpressionEvaluator.GetValue(null, "1 between {1, 5}") // true
|
||||
|
||||
ExpressionEvaluator.GetValue(null, "'efg' between {'abc', 'xyz'}") // true
|
||||
|
||||
ExpressionEvaluator.GetValue(null, "'xyz' is int") // false
|
||||
|
||||
ExpressionEvaluator.GetValue(null, "{1, 2, 3, 4, 5} is IList") // true
|
||||
|
||||
ExpressionEvaluator.GetValue(null, "'5.0067' matches '^-?\\d+(\\.\\d{2})?$'")) // false
|
||||
|
||||
ExpressionEvaluator.GetValue(null, @"'5.00' matches '^-?\d+(\.\d{2})?$'") // true</programlisting>Note
|
||||
that the Visual Basic and not SQL syntax is used for the
|
||||
<emphasis>like</emphasis> operator pattern string.
|
||||
</para>
|
||||
</sect3>
|
||||
|
||||
<sect3 id="expressions-logical">
|
||||
<title>Logical operators</title>
|
||||
|
||||
<para>The logical operators that are supported are
|
||||
<emphasis>and</emphasis>, <emphasis>or</emphasis>, and
|
||||
<emphasis>not</emphasis>. Their use is demonstrated
|
||||
below<programlisting>// AND
|
||||
bool falseValue = (bool) ExpressionEvaluator.GetValue(null, "true and false"); //false
|
||||
|
||||
string expression = @"IsMember('Nikola Tesla') and IsMember('Mihajlo Pupin')";
|
||||
bool trueValue = (bool) ExpressionEvaluator.GetValue(ieee, expression); //true
|
||||
|
||||
// OR
|
||||
bool trueValue = (bool) ExpressionEvaluator.GetValue(null, "true or false"); //true
|
||||
|
||||
string expression = @"IsMember('Nikola Tesla') or IsMember('Albert Einstien')";
|
||||
bool trueValue = (bool) ExpressionEvaluator.GetValue(ieee, expression); // true
|
||||
|
||||
// NOT
|
||||
bool falseValue = (bool) ExpressionEvaluator.GetValue(null, "!true");
|
||||
|
||||
// AND and NOT
|
||||
string expression = @"IsMember('Nikola Tesla') and !IsMember('Mihajlo Pupin')";
|
||||
bool falseValue = (bool) ExpressionEvaluator.GetValue(ieee, expression);</programlisting></para>
|
||||
</sect3>
|
||||
|
||||
<sect3 id="expressions-math">
|
||||
<title>Mathematical operators</title>
|
||||
|
||||
<para>The addition operator can be used on numbers, strings and dates.
|
||||
Subtraction can be used on numbers and dates. Multiplication and
|
||||
division can be used only on numbers. Other mathematical operators
|
||||
supported are modulus (%) and exponential power (^). Standard operator
|
||||
precedence is enforced. These operators are demonstrated below
|
||||
<programlisting>// Addition
|
||||
int two = (int)ExpressionEvaluator.GetValue(null, "1 + 1"); // 2
|
||||
|
||||
String testString = (String)ExpressionEvaluator.GetValue(null, "'test' + ' ' + 'string'"); //'test string'
|
||||
|
||||
DateTime dt = (DateTime)ExpressionEvaluator.GetValue(null, "date('1974-08-24') + 5"); // 8/29/1974
|
||||
|
||||
// Subtraction
|
||||
|
||||
int four = (int) ExpressionEvaluator.GetValue(null, "1 - -3"); //4
|
||||
|
||||
Decimal dec = (Decimal) ExpressionEvaluator.GetValue(null, "1000.00m - 1e4"); // 9000.00
|
||||
|
||||
TimeSpan ts = (TimeSpan) ExpressionEvaluator.GetValue(null, "date('2004-08-14') - date('1974-08-24')"); //10948.00:00:00
|
||||
|
||||
// Multiplication
|
||||
|
||||
int six = (int) ExpressionEvaluator.GetValue(null, "-2 * -3"); // 6
|
||||
|
||||
int twentyFour = (int) ExpressionEvaluator.GetValue(null, "2.0 * 3e0 * 4"); // 24
|
||||
|
||||
// Division
|
||||
|
||||
int minusTwo = (int) ExpressionEvaluator.GetValue(null, "6 / -3"); // -2
|
||||
|
||||
int one = (int) ExpressionEvaluator.GetValue(null, "8.0 / 4e0 / 2"); // 1
|
||||
|
||||
// Modulus
|
||||
|
||||
int three = (int) ExpressionEvaluator.GetValue(null, "7 % 4"); // 3
|
||||
|
||||
int one = (int) ExpressionEvaluator.GetValue(null, "8.0 % 5e0 % 2"); // 1
|
||||
|
||||
// Exponent
|
||||
|
||||
int sixteen = (int) ExpressionEvaluator.GetValue(null, "-2 ^ 4"); // 16
|
||||
|
||||
// Operator precedence
|
||||
|
||||
int minusFortyFive = (int) ExpressionEvaluator.GetValue(null, "1+2-3*8^2/2/2"); // -45
|
||||
</programlisting></para>
|
||||
</sect3>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="expressions-assignment">
|
||||
<title>Assignment</title>
|
||||
|
||||
<para>Setting of a property is done by using the assignment operator.
|
||||
This would typically be done within a call to
|
||||
<literal>GetValue</literal> since in the simple case
|
||||
<literal>SetValue</literal> offers the same functionality. Assignment in
|
||||
this manner is useful when combining multiple operators in an expression
|
||||
list, discussed in the next section. Some examples of assignment are
|
||||
shown below <programlisting>Inventor inventor = new Inventor();
|
||||
String aleks = (String) ExpressionEvaluator.GetValue(inventor, "Name = 'Aleksandar Seovic'");
|
||||
DateTime dt = (DateTime) ExpressionEvaluator.GetValue(inventor, "DOB = date('1974-08-24')");
|
||||
|
||||
//Set the vice president of the society
|
||||
Inventor tesla = (Inventor) ExpressionEvaluator.GetValue(ieee, "Officers['vp'] = Members[0]");</programlisting></para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="expressions-explist">
|
||||
<title>Expression lists</title>
|
||||
|
||||
<para>Multiple expressions can be evaluated against the same context
|
||||
object by separating them with a semicolon and enclosing the entire
|
||||
expression within parentheses. The value returned is the value of the
|
||||
last expression in the list. Examples of this are shown below
|
||||
<programlisting>//Perform property assignments and then return Name property.
|
||||
|
||||
String pupin = (String) ExpressionEvaluator.GetValue(ieee.Members,
|
||||
"( [1].PlaceOfBirth.City = 'Beograd'; [1].PlaceOfBirth.Country = 'Serbia'; [1].Name )"));
|
||||
|
||||
// pupin = "Mihajlo Pupin"</programlisting></para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="expressions-types">
|
||||
<title>Types</title>
|
||||
|
||||
<para>In many cases, you can reference types by simply specifying type
|
||||
name:<programlisting>ExpressionEvaluator.GetValue(null, "1 is int")
|
||||
|
||||
ExpressionEvaluator.GetValue(null, "DateTime.Today")
|
||||
|
||||
ExpressionEvaluator.GetValue(null, "new string[] {'abc', 'efg'}")</programlisting></para>
|
||||
|
||||
<para>This is possible for all standard types from
|
||||
<literal>mscorlib</literal>, as well as for any other type that is
|
||||
registered with the <literal>TypeRegistry</literal> as described in the
|
||||
next section.</para>
|
||||
|
||||
<para>For all other types, you need to use special
|
||||
<literal>T(typeName)</literal> expression:<programlisting>Type dateType = (Type) ExpressionEvaluator.GetValue(null, "T(System.DateTime)")
|
||||
|
||||
Type evalType = (Type) ExpressionEvaluator.GetValue(null, "T(Spring.Expressions.ExpressionEvaluator, Spring.Core)")
|
||||
|
||||
bool trueValue = (bool) ExpressionEvaluator.GetValue(tesla, "T(System.DateTime) == DOB.GetType()")</programlisting></para>
|
||||
|
||||
<note>
|
||||
<para>The implementation delegates to Spring's
|
||||
<classname>ObjectUtils.ResolveType</classname> method for the actual
|
||||
type resolution, which means that the types used within expressions
|
||||
are resolved in the exactly the same way as the types specified in
|
||||
Spring configuration files.</para>
|
||||
</note>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="expressions-typeregistration">
|
||||
<title>Type Registration</title>
|
||||
|
||||
<para>To refer to a type within an expression that is not in the
|
||||
mscorlib you need to register it with the
|
||||
<literal>TypeRegistry</literal>. This will allow you to refer to a
|
||||
shorthand name of the type within your expressions. This is commonly
|
||||
used in expression that use the new operator or refer to a static
|
||||
properties of an object. Example usage is shown below.</para>
|
||||
|
||||
<programlisting>TypeRegistry.RegisterType("Society", typeof(Society));
|
||||
|
||||
Inventor pupin = (Inventor) ExpressionEvaluator.GetValue(ieee, "Officers[Society.President]");</programlisting>
|
||||
|
||||
<para>Alternatively, you can register types using
|
||||
<literal>typeAliases</literal> configuration section.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="expressions-ctor">
|
||||
<title>Constructors</title>
|
||||
|
||||
<para>Constructors can be invoked using the new operator. For classes
|
||||
outside mscorlib you will need to register your types so they can be
|
||||
resolved. Examples of using constructors are shown below:
|
||||
<programlisting>// simple ctor
|
||||
DateTime dt = (DateTime) ExpressionEvaluator.GetValue(null, "new DateTime(1974, 8, 24)");
|
||||
|
||||
// Register Inventor type then create new inventor instance within Add method inside an expression list.
|
||||
// Then return the new count of the Members collection.
|
||||
|
||||
TypeRegistry.RegisterType(typeof(Inventor));
|
||||
int three = (int) ExpressionEvaluator.GetValue(ieee.Members, "{ Add(new Inventor('Aleksandar Seovic', date('1974-08-24'), 'Serbian')); Count}"));
|
||||
|
||||
</programlisting></para>
|
||||
|
||||
<para>As a convenience, Spring.NET also allows you to define named
|
||||
constructor arguments, which are used to set object's properties after
|
||||
instantiation, similar to the way standard .NET attributes work. For
|
||||
example, you could create an instance of the <literal>Inventor</literal>
|
||||
class and set its <literal>Inventions</literal> property in a single
|
||||
statement:<programlisting>
|
||||
Inventor aleks = (Inventor) ExpressionEvaluator.GetValue(null, "new Inventor('Aleksandar Seovic', date('1974-08-24'), 'Serbian', Inventions = {'SPELL'})");
|
||||
</programlisting>The only rule you have to follow is that named arguments
|
||||
should be specified <emphasis>after</emphasis> standard constructor
|
||||
arguments, just like in the .NET attributes.</para>
|
||||
|
||||
<para>While we are on the subject, Spring.NET Expression Language also
|
||||
provides a convenient syntax for .NET attribute instance creation.
|
||||
Instead of using standard constructor syntax, you can use a somewhat
|
||||
shorter and more familiar syntax to create an instance of a .NET
|
||||
attribute class:<programlisting>
|
||||
WebMethodAttribute webMethod = (WebMethodAttribute) ExpressionEvaluator.GetValue(null, "@[WebMethod(true, CacheDuration = 60, Description = 'My Web Method')]");
|
||||
</programlisting>As you can see, with the exception of the
|
||||
<literal>@</literal> prefix, syntax is exactly the same as in C#.</para>
|
||||
|
||||
<para>Slightly different syntax is not the only thing that
|
||||
differentiates an attribute expression from a standard constructor
|
||||
invocation expression. In addition to that, attribute expression uses
|
||||
slightly different type resolution mechanism and will attempt to load
|
||||
both the specified type name and the specified type name with an
|
||||
<literal>Attribute</literal> suffix, just like the C# compiler.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="expressions-variables">
|
||||
<title>Variables</title>
|
||||
|
||||
<para>Variables can referenced in the expression using the syntax
|
||||
<literal>#</literal><emphasis>variableName</emphasis>. The variables are
|
||||
passed in and out of the expression using the dictionary parameter in
|
||||
<classname>ExpressionEvaluator</classname>'s <literal>GetValue</literal>
|
||||
or <literal>SetValue</literal> methods. <programlisting>public static object GetValue(object root, string expression, IDictionary variables)
|
||||
|
||||
public static void SetValue(object root, string expression, IDictionary variables, object newValue)</programlisting>
|
||||
The variable name is the key value of the dictionary. Example usage is
|
||||
shown below; <programlisting>IDictionary vars = new Hashtable();
|
||||
vars["newName"] = "Mike Tesla";
|
||||
ExpressionEvaluator.GetValue(tesla, "Name = #newName", vars));</programlisting>
|
||||
You can also use the dictionary as a place to store values of the object
|
||||
as they are evaluated inside the expression. For example to change
|
||||
Tesla's first name back again and keep the old value; <programlisting>ExpressionEvaluator.GetValue(tesla, "{ #oldName = Name; Name = 'Nikola Tesla' }", vars);
|
||||
String oldName = (String)vars["oldName"]; // Mike Tesla</programlisting>
|
||||
Variable names can also be used inside indexers or maps instead of
|
||||
literal values. For example; <programlisting>vars["prez"] = "president";
|
||||
Inventor pupin = (Inventor) ExpressionEvaluator.GetValue(ieee, "Officers[#prez]", vars);</programlisting></para>
|
||||
|
||||
<sect3 id="expressions-this">
|
||||
<title>The '#this' and '#root' variables</title>
|
||||
|
||||
<para>There are two special variables that are always defined and can
|
||||
be references within the expression: <literal>#this</literal> and
|
||||
<literal>#root</literal>.</para>
|
||||
|
||||
<para>The <literal>#this</literal> variable can be used to explicitly
|
||||
refer to the context for the node that is currently being
|
||||
evaluated:<programlisting>// sets the name of the president and returns its instance
|
||||
ExpressionEvaluator.GetValue(ieee, "Officers['president'].( #this.Name = 'Nikola Tesla'; #this )")</programlisting></para>
|
||||
|
||||
<para>Similarly, the <literal>#root</literal> variable allows you to
|
||||
refer to the root context for the expression:<programlisting>// removes president from the Officers dictionary and returns removed instance
|
||||
ExpressionEvaluator.GetValue(ieee, "Officers['president'].( #root.Officers.Remove('president'); #this )")</programlisting></para>
|
||||
</sect3>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="expressions-ternary">
|
||||
<title>Ternary Operator (If-Then-Else)</title>
|
||||
|
||||
<para>You can use the ternary operator for performing if-then-else
|
||||
conditional logic inside the expression. A minimal example is;
|
||||
<programlisting>String aTrueString = (String) ExpressionEvaluator.GetValue(null, "false ? 'trueExp' : 'falseExp'") // trueExp
|
||||
</programlisting> In this case, the boolean false results in returning the
|
||||
string value 'trueExp'. A less artificial example is shown below
|
||||
<programlisting>ExpressionEvaluator.SetValue(ieee, "Name", "IEEE");
|
||||
IDictionary vars = new Hashtable();
|
||||
vars["queryName"] = "Nikola Tesla";
|
||||
|
||||
string expression = @"IsMember(#queryName)
|
||||
? #queryName + ' is a member of the ' + Name + ' Society'
|
||||
: #queryName + ' is not a member of the ' + Name + ' Society'";
|
||||
|
||||
String queryResultString = (String) ExpressionEvaluator.GetValue(ieee, expression, vars));
|
||||
|
||||
// queryResultString = "Nikola Tesla is a member of the IEEE Society"</programlisting></para>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>List Projection and Selection</title>
|
||||
|
||||
<para>List projection and selection are very powerful expression
|
||||
language features that allow you to transform the source list into
|
||||
another list by either <emphasis>projecting</emphasis> across its
|
||||
"columns", or <emphasis>selecting</emphasis> from its "rows". In other
|
||||
words, projection can be thought of as a column selector in a SQL SELECT
|
||||
statement, while selection would be comparable to the WHERE
|
||||
clause.</para>
|
||||
|
||||
<para>For example, let's say that we need a list of the cities where our
|
||||
inventors were born. This could be easily obtained by projecting on the
|
||||
<literal>PlaceOfBirth.City</literal> property: <programlisting>IList placesOfBirth = (IList) ExpressionEvaluator.GetValue(ieee, "Members.!{PlaceOfBirth.City}") // { 'Smiljan', 'Idvor' }
|
||||
</programlisting>Or we can get the list of officers' names:<programlisting>IList officersNames = (IList) ExpressionEvaluator.GetValue(ieee, "Officers.Values.!{Name}") // { 'Nikola Tesla', 'Mihajlo Pupin' }
|
||||
</programlisting></para>
|
||||
|
||||
<para>As you can see from the examples, projection uses
|
||||
<literal>!{</literal><emphasis>projectionExpression</emphasis><literal>}</literal>
|
||||
syntax and will return a new list of the same length as the original
|
||||
list but typically with the elements of a different type.</para>
|
||||
|
||||
<para>On the other hand, selection, which uses
|
||||
<literal>?{</literal><emphasis>projectionExpression</emphasis><literal>}</literal>
|
||||
syntax, will filter the list and return a new list containing a subset
|
||||
of the original element list. For example, selection would allow us to
|
||||
easily get a list of Serbian inventors:<programlisting>IList serbianInventors = (IList) ExpressionEvaluator.GetValue(ieee, "Members.?{Nationality == 'Serbian'}") // { tesla, pupin }
|
||||
</programlisting>Or to get a list of inventors that invented
|
||||
sonar:<programlisting>IList sonarInventors = (IList) ExpressionEvaluator.GetValue(ieee, "Members.?{'Sonar' in Inventions}") // { pupin }
|
||||
</programlisting>Or we can combine selection and projection to get a list of
|
||||
sonar inventors' names:<programlisting>IList sonarInventorsNames = (IList) ExpressionEvaluator.GetValue(ieee, "Members.?{'Sonar' in Inventions}.!{Name}") // { 'Mihajlo Pupin' }
|
||||
</programlisting></para>
|
||||
|
||||
<para>As a convenience, Spring.NET Expression Language also supports a
|
||||
special syntax for selecting the first or last match. Unlike regular
|
||||
selection, which will return an empty list if no matches are found,
|
||||
first or last match selection expression will either return an instance
|
||||
of the matched element, or <literal>null</literal> if no matching
|
||||
elements were found. In order to return a first match you should prefix
|
||||
your selection expression with <literal>^{</literal> instead of
|
||||
<literal>?{</literal>, and to return last match you should use
|
||||
<literal>${</literal> prefix:<programlisting>ExpressionEvaluator.GetValue(ieee, "Members.^{Nationality == 'Serbian'}.Name") // 'Nikola Tesla'
|
||||
ExpressionEvaluator.GetValue(ieee, "Members.${Nationality == 'Serbian'}.Name") // 'Mihajlo Pupin'
|
||||
</programlisting>Notice that we access the <literal>Name</literal> property
|
||||
directly on the selection result, because an actual matched instance is
|
||||
returned by the first and last match expression instead of a filtered
|
||||
list.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Collection Processors and Aggregators</title>
|
||||
|
||||
<para>In addition to list projection and selection, Spring.NET
|
||||
Expression Language also supports several collection processors, such as
|
||||
<literal>distinct</literal>, <literal>nonNull</literal> and
|
||||
<literal>sort</literal>, as well as a number of commonly used
|
||||
aggregators, such as <literal>max</literal>, <literal>min</literal>,
|
||||
<literal>count</literal>, <literal>sum</literal> and
|
||||
<literal>average</literal>.</para>
|
||||
|
||||
<para>The difference between processors and aggregators is that
|
||||
processors return a new or transformed collection, while aggregators
|
||||
return a single value. Other than that, they are very similar -- both
|
||||
processors and aggregators are invoked on a collection node using
|
||||
standard method invocation expression syntax, which makes them very
|
||||
simple to use and allows easy chaining of multiple processors.</para>
|
||||
|
||||
<sect3>
|
||||
<title>Count Aggregator</title>
|
||||
|
||||
<para>The count aggregator is a safe way to obtain a number of items
|
||||
in a collection. It can be applied to a collection of any type,
|
||||
including arrays, which helps eliminate the decision on whether to use
|
||||
<literal>Count</literal> or <literal>Length</literal> property
|
||||
depending on the context. Unlike its standard .NET counterparts, count
|
||||
aggregator can also be invoked on the <literal>null</literal> context
|
||||
without throwing a <classname>NullReferenceException</classname>. It
|
||||
will simply return zero in this case, which makes it much safer than
|
||||
standard .NET properties within larger expression.<programlisting>ExpressionEvaluator.GetValue(null, "{1, 5, -3}.count()") // 3
|
||||
ExpressionEvaluator.GetValue(null, "count()") // 0
|
||||
</programlisting></para>
|
||||
</sect3>
|
||||
|
||||
<sect3>
|
||||
<title>Sum Aggregator</title>
|
||||
|
||||
<para>The sum aggregator can be used to calculate a total for the list
|
||||
of numeric values. If numbers within the list are not of the same type
|
||||
or precision, it will automatically perform necessary conversion and
|
||||
the result will be the highest precision type. If any of the
|
||||
collection elements is not a number, this aggregator will throw an
|
||||
<classname>InvalidArgumentException</classname>.<programlisting>ExpressionEvaluator.GetValue(null, "{1, 5, -3, 10}.sum()") // 13 (int)
|
||||
ExpressionEvaluator.GetValue(null, "{5, 5.8, 12.2, 1}.sum()") // 24.0 (double)
|
||||
</programlisting></para>
|
||||
</sect3>
|
||||
|
||||
<sect3>
|
||||
<title>Average Aggregator</title>
|
||||
|
||||
<para>The average aggregator will return the average for the
|
||||
collection of numbers. It will use the same type coercion rules, as
|
||||
the sum aggregator in order to be as precise as possible. Just like
|
||||
the sum aggregator, if any of the collection elements is not a number,
|
||||
it will throw an
|
||||
<classname>InvalidArgumentException</classname>.<programlisting>ExpressionEvaluator.GetValue(null, "{1, 5, -4, 10}.average()") // 3
|
||||
ExpressionEvaluator.GetValue(null, "{1, 5, -2, 10}.average()") // 3.5
|
||||
</programlisting></para>
|
||||
</sect3>
|
||||
|
||||
<sect3>
|
||||
<title>Minimum Aggregator</title>
|
||||
|
||||
<para>The minimum aggregator will return the smallest item in the
|
||||
list. In order to determine what "the smallest" actually means, this
|
||||
aggregator relies on the assumption that the collection items are of
|
||||
the uniform type and that they implement the
|
||||
<classname>IComparable</classname> interface. If that is not the case,
|
||||
this aggregator will throw an
|
||||
<classname>InvalidArgumentException</classname>.<programlisting>ExpressionEvaluator.GetValue(null, "{1, 5, -3, 10}.min()") // -3
|
||||
ExpressionEvaluator.GetValue(null, "{'abc', 'efg', 'xyz'}.min()") // 'abc'
|
||||
</programlisting></para>
|
||||
</sect3>
|
||||
|
||||
<sect3>
|
||||
<title>Maximum Aggregator</title>
|
||||
|
||||
<para>The maximum aggregator will return the largest item in the list.
|
||||
In order to determine what "the largest" actually means, this
|
||||
aggregator relies on the assumption that the collection items are of
|
||||
the uniform type and that they implement
|
||||
<classname>IComparable</classname> interface. If that is not the case,
|
||||
this aggregator will throw an
|
||||
<classname>InvalidArgumentException</classname>.<programlisting>ExpressionEvaluator.GetValue(null, "{1, 5, -3, 10}.max()") // 10
|
||||
ExpressionEvaluator.GetValue(null, "{'abc', 'efg', 'xyz'}.max()") // 'xyz'
|
||||
</programlisting></para>
|
||||
</sect3>
|
||||
|
||||
<sect3>
|
||||
<title>Non-null Processor</title>
|
||||
|
||||
<para>A non-null processor is a very simple collection processor that
|
||||
eliminates all <literal>null</literal> values from the
|
||||
collection.<programlisting>ExpressionEvaluator.GetValue(null, "{ 'abc', 'xyz', null, 'abc', 'def', null}.nonNull()") // { 'abc', 'xyz', 'abc', 'def' }
|
||||
ExpressionEvaluator.GetValue(null, "{ 'abc', 'xyz', null, 'abc', 'def', null}.nonNull().distinct().sort()") // { 'abc', 'def', 'xyz' }
|
||||
</programlisting></para>
|
||||
</sect3>
|
||||
|
||||
<sect3>
|
||||
<title>Distinct Processor</title>
|
||||
|
||||
<para>A distinct processor is very useful when you want to ensure that
|
||||
you don't have duplicate items in the collection. It can also accept
|
||||
an optional <literal>Boolean</literal> argument that will determine
|
||||
whether <literal>null</literal> values should be included in the
|
||||
results. The default is <literal>false</literal>, which means that
|
||||
they will not be included. <programlisting>ExpressionEvaluator.GetValue(null, "{ 'abc', 'xyz', 'abc', 'def', null, 'def' }.distinct(true).sort()") // { null, 'abc', 'def', 'xyz' }
|
||||
ExpressionEvaluator.GetValue(null, "{ 'abc', 'xyz', 'abc', 'def', null, 'def' }.distinct(false).sort()") // { 'abc', 'def', 'xyz' }
|
||||
</programlisting></para>
|
||||
</sect3>
|
||||
|
||||
<sect3>
|
||||
<title>Sort Processor</title>
|
||||
|
||||
<para>The sort processor can be used to sort uniform collections of
|
||||
elements that implement <classname>IComparable</classname>.</para>
|
||||
|
||||
<programlisting>ExpressionEvaluator.GetValue(null, "{1.2, 5.5, -3.3}.sort()") // { -3.3, 1.2, 5.5 }
|
||||
ExpressionEvaluator.GetValue(null, "{ 'abc', 'xyz', 'abc', 'def', null, 'def' }.sort()") // { null, 'abc', 'abc', 'def', 'def', 'xyz' }
|
||||
</programlisting>
|
||||
</sect3>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Spring Object References</title>
|
||||
|
||||
<para>Expressions can refer to objects that are declared in Spring's
|
||||
application context using the syntax
|
||||
<literal>@(</literal><emphasis>contextName</emphasis><literal>:</literal><emphasis>objectName</emphasis><literal>)</literal>.
|
||||
If no contextName is specified the default root context name
|
||||
(<literal>Spring.RootContext</literal>) is used. Using the application
|
||||
context defined in the MovieFinder example from <xref
|
||||
linkend="quickstarts" />, the following expression returns the number of
|
||||
movies directed by Roberto Benigni. <programlisting>public static void Main()
|
||||
{
|
||||
. . .
|
||||
|
||||
// Retrieve context defined in the spring/context section of
|
||||
// the standard .NET configuration file.
|
||||
IApplicationContext ctx = ContextRegistry.GetContext();
|
||||
|
||||
int numMovies = (int) ExpressionEvaluator.GetValue(null,
|
||||
"@(MyMovieLister).MoviesDirectedBy('Roberto Benigni').Length");
|
||||
|
||||
. . .
|
||||
}</programlisting> The variable numMovies is evaluated to 2 in this
|
||||
example.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Lambda Expressions</title>
|
||||
|
||||
<para>A somewhat advanced, but a very powerful feature of Spring.NET
|
||||
Expression Language are lambda expressions. Lambda expressions allow you
|
||||
to define inline functions, which can then be used within your
|
||||
expressions just like any other function or method.</para>
|
||||
|
||||
<para>The syntax for defining lambda expressions is:</para>
|
||||
|
||||
<para><literal>#</literal><emphasis>functionName</emphasis><literal> =
|
||||
{|</literal><emphasis>argList</emphasis><literal>|
|
||||
</literal><emphasis>functionBody</emphasis><literal> }</literal></para>
|
||||
|
||||
<para>For example, you could define a <literal>max</literal> function
|
||||
and call it like this:<programlisting>ExpressionEvaluator.GetValue(null, "(#max = {|x,y| $x > $y ? $x : $y }; #max(5,25))", new Hashtable()) // 25</programlisting></para>
|
||||
|
||||
<para>As you can see, any arguments defined for the expression can be
|
||||
referenced within the function body using a <emphasis>local
|
||||
variable</emphasis> syntax,
|
||||
<literal>$</literal><emphasis>varName</emphasis>. Invocation of the
|
||||
function defined using lambda expression is as simple as specifying the
|
||||
comma-separated list of function arguments in parentheses, after the
|
||||
function name.</para>
|
||||
|
||||
<para>Lambda expressions can be recursive, which means that you can
|
||||
invoke the function within its own body:<programlisting>ExpressionEvaluator.GetValue(null, "(#fact = {|n| $n <= 1 ? 1 : $n * #fact($n-1) }; #fact(5))", new Hashtable()) // 120</programlisting></para>
|
||||
|
||||
<para>Notice that in both examples above we had to specify a
|
||||
<literal>variables</literal> parameter for the
|
||||
<literal>GetValue</literal> method. This is because lambda expressions
|
||||
are actually nothing more than parameterized variables and we need
|
||||
variables dictionary in order to store them. If you don't specify a
|
||||
valid <literal>IDictionary</literal> instance for the
|
||||
<literal>variables</literal> parameter, you will get a runtime
|
||||
exception.</para>
|
||||
|
||||
<para>Also, in both examples above we used an expression list in order
|
||||
to define and invoke a function in a single expression. However, more
|
||||
likely than not, you will want to define your functions once and then
|
||||
use them within as many expressions as you need. Spring.NET provides an
|
||||
easy way to pre-register your lambda expressions by exposing a static
|
||||
<literal>Expression.RegisterFunction</literal> method, which takes
|
||||
function name, lambda expression and variables dictionary to register
|
||||
function in as parameters:<programlisting>IDictionary vars = new Hashtable();
|
||||
Expression.RegisterFunction("sqrt", "{|n| Math.Sqrt($n)}", vars);
|
||||
Expression.RegisterFunction("fact", "{|n| $n <= 1 ? 1 : $n * #fact($n-1)}", vars);</programlisting>Once
|
||||
the function registration is done, you can simply evaluate an expression
|
||||
that uses these functions, making sure that the <literal>vars</literal>
|
||||
dictionary is passed as a parameter to expression evaluation
|
||||
engine:<programlisting>ExpressionEvaluator.GetValue(null, "#fact(5)", vars) // 120
|
||||
ExpressionEvaluator.GetValue(null, "#sqrt(9)", vars) // 3</programlisting></para>
|
||||
|
||||
<para>Finally, because lambda expressions are treated as variables, they
|
||||
can be assigned to other variables or passed as parameters to other
|
||||
lambda expressions. In the following example we are defining a delegate
|
||||
function that accepts function <literal>f</literal> as the first
|
||||
argument and parameter <literal>n</literal> that will be passed to
|
||||
function <literal>f</literal> as the second. Then we invoke the
|
||||
functions registered in the previous example, as well as the lambda
|
||||
expression defined inline, through our delegate:<programlisting>Expression.RegisterFunction("delegate", "{|f, n| $f($n) }", vars);
|
||||
ExpressionEvaluator.GetValue(null, "#delegate(#sqrt, 4)", vars) // 2
|
||||
ExpressionEvaluator.GetValue(null, "#delegate(#fact, 5)", vars) // 120
|
||||
ExpressionEvaluator.GetValue(null, "#delegate({|n| $n ^ 2 }, 5)", vars) // 25</programlisting>While
|
||||
this particular example is not particularly useful, it does demonstrate
|
||||
that lambda expressions are indeed treated as nothing more than
|
||||
parameterized variables, which is important to remember.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>Null Context</title>
|
||||
|
||||
<para>If you do not specify a root object, i.e. pass in null, then the
|
||||
expressions evaluated either have to be literal values, i.e.
|
||||
ExpressionEvaluator.GetValue(null, "2 + 3.14"), refer to classes that
|
||||
have static methods or properties, i.e.
|
||||
ExpressionEvaluator.GetValue(null, "DateTime.Today"), create new
|
||||
instances of objects, i.e. ExpressionEvaluator.GetValue(null, "new
|
||||
DateTime(2004, 8, 14)") or refer to other objects such as those in the
|
||||
variable dictionary or in the IoC container. The latter two usages will
|
||||
be discussed later.</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<!-- SAMPLE CLASSES AND DATA -->
|
||||
|
||||
<sect1 id="expressions-classes">
|
||||
<title>Classes used in the examples</title>
|
||||
|
||||
<para>The following simple classes are used to demonstrate the
|
||||
functionality of the expression language.</para>
|
||||
|
||||
<programlisting>public class Inventor
|
||||
{
|
||||
public string Name;
|
||||
public string Nationality;
|
||||
public string[] Inventions;
|
||||
private DateTime dob;
|
||||
private Place pob;
|
||||
|
||||
public Inventor() : this(null, DateTime.MinValue, null)
|
||||
{}
|
||||
|
||||
public Inventor(string name, DateTime dateOfBirth, string nationality)
|
||||
{
|
||||
this.Name = name;
|
||||
this.dob = dateOfBirth;
|
||||
this.Nationality = nationality;
|
||||
this.pob = new Place();
|
||||
}
|
||||
|
||||
public DateTime DOB
|
||||
{
|
||||
get { return dob; }
|
||||
set { dob = value; }
|
||||
}
|
||||
|
||||
public Place PlaceOfBirth
|
||||
{
|
||||
get { return pob; }
|
||||
}
|
||||
|
||||
public int GetAge(DateTime on)
|
||||
{
|
||||
// not very accurate, but it will do the job ;-)
|
||||
return on.Year - dob.Year;
|
||||
}
|
||||
}
|
||||
|
||||
public class Place
|
||||
{
|
||||
public string City;
|
||||
public string Country;
|
||||
}
|
||||
|
||||
public class Society
|
||||
{
|
||||
public string Name;
|
||||
public static string Advisors = "advisors";
|
||||
public static string President = "president";
|
||||
|
||||
private IList members = new ArrayList();
|
||||
private IDictionary officers = new Hashtable();
|
||||
|
||||
public IList Members
|
||||
{
|
||||
get { return members; }
|
||||
}
|
||||
|
||||
public IDictionary Officers
|
||||
{
|
||||
get { return officers; }
|
||||
}
|
||||
|
||||
public bool IsMember(string name)
|
||||
{
|
||||
bool found = false;
|
||||
foreach (Inventor inventor in members)
|
||||
{
|
||||
if (inventor.Name == name)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
}</programlisting>
|
||||
|
||||
<para>The code listings in this chapter use instances of the data
|
||||
populated with the following information.</para>
|
||||
|
||||
<programlisting>Inventor tesla = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian");
|
||||
tesla.Inventions = new string[]
|
||||
{
|
||||
"Telephone repeater", "Rotating magnetic field principle",
|
||||
"Polyphase alternating-current system", "Induction motor",
|
||||
"Alternating-current power transmission", "Tesla coil transformer",
|
||||
"Wireless communication", "Radio", "Fluorescent lights"
|
||||
};
|
||||
tesla.PlaceOfBirth.City = "Smiljan";
|
||||
|
||||
Inventor pupin = new Inventor("Mihajlo Pupin", new DateTime(1854, 10, 9), "Serbian");
|
||||
pupin.Inventions = new string[] {"Long distance telephony & telegraphy", "Secondary X-Ray radiation", "Sonar"};
|
||||
pupin.PlaceOfBirth.City = "Idvor";
|
||||
pupin.PlaceOfBirth.Country = "Serbia";
|
||||
|
||||
Society ieee = new Society();
|
||||
ieee.Members.Add(tesla);
|
||||
ieee.Members.Add(pupin);
|
||||
ieee.Officers["president"] = pupin;
|
||||
ieee.Officers["advisors"] = new Inventor[] {tesla, pupin};</programlisting>
|
||||
</sect1>
|
||||
</chapter>
|
||||
BIN
doc/reference/src/images/Copy of S2-banner-rhs.png
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
BIN
doc/reference/src/images/DataAccessException.gif
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
doc/reference/src/images/S2-banner-rhs.png
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
BIN
doc/reference/src/images/Thumbs.db
Normal file
BIN
doc/reference/src/images/aop-chain.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
doc/reference/src/images/aop-uml.gif
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
BIN
doc/reference/src/images/bean-lifecycle-overview.gif
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
BIN
doc/reference/src/images/container-in-action.gif
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
doc/reference/src/images/i21-banner-rhs.jpg
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
doc/reference/src/images/link.png
Normal file
|
After Width: | Height: | Size: 376 B |
BIN
doc/reference/src/images/logo.gif
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
BIN
doc/reference/src/images/logo.jpg
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
doc/reference/src/images/logo.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
doc/reference/src/images/logo.psd
Normal file
BIN
doc/reference/src/images/logo.xcf
Normal file
BIN
doc/reference/src/images/movie-finder.gif
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
doc/reference/src/images/overview.gif
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
doc/reference/src/images/remoting-solution.gif
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
doc/reference/src/images/remoting-startup.gif
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
doc/reference/src/images/spring-triangle.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
doc/reference/src/images/spring.sxd
Normal file
BIN
doc/reference/src/images/spring.vsd
Normal file
BIN
doc/reference/src/images/spring.windows-service.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
doc/reference/src/images/spring.windows-service.vsd
Normal file
BIN
doc/reference/src/images/tx.png
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
doc/reference/src/images/web-exporter-calc-svc-aop-add.jpg
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
doc/reference/src/images/web-exporter-calc-svc-aop-add001.jpg
Normal file
|
After Width: | Height: | Size: 192 KiB |
BIN
doc/reference/src/images/web-exporter-calc-svc-aop.jpg
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
doc/reference/src/images/web-exporter-calc-svc-main.jpg
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
doc/reference/src/images/web-exporter-calc-svc.jpg
Normal file
|
After Width: | Height: | Size: 58 KiB |