2295 lines
97 KiB
XML
2295 lines
97 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!--
|
|
/*
|
|
* Copyright 2002-2010 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 version="5" xml:id="ado" xmlns="http://docbook.org/ns/docbook"
|
|
xmlns:ns5="http://www.w3.org/1999/xhtml"
|
|
xmlns:ns4="http://www.w3.org/1998/Math/MathML"
|
|
xmlns:ns3="http://www.w3.org/2000/svg"
|
|
xmlns:ns2="http://www.w3.org/1999/xlink"
|
|
xmlns:ns="http://docbook.org/ns/docbook">
|
|
<title>Data access using ADO.NET</title>
|
|
|
|
<sect1 xml:id="ado-introduction">
|
|
<title>Introduction</title>
|
|
|
|
<para>Spring provides an abstraction for data access via ADO.NET that
|
|
provides the following benefits and features</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>Consistent and comprehensive database provider interfaces for
|
|
both .NET 1.1 and 2.0</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Integration with Spring's transaction management
|
|
features.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Template style use of DbCommand that removes the need to write
|
|
typical ADO.NET boiler-plate code.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>'One-liner' implementations for the most common database usage
|
|
patterns lets you focus on the 'meat' of your ADO.NET code.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Easy database parameter creation/management</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Provider independent exceptions with database error codes and
|
|
higher level DAO exception hierarchy.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Centralized resource management for connections, commands, data
|
|
readers, etc.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Simple DataReader to Object mapping framework.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>This chapter is divided up into a number of sections that describe
|
|
the major areas of functionality within Spring's ADO.NET support.</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>Motivations - describes why one should consider using Spring's
|
|
ADO.NET features as compared to using 'raw' ADO.NET API.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Provider Abstraction - a quick overview of Spring's provider
|
|
abstraction.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Approaches to ADO.NET Data Access - Discusses the two styles of
|
|
Spring's ADO.NET data access classes - template and object
|
|
based.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Introduction to AdoTemplate - Introduction to the design and
|
|
core methods of the central class in Spring's ADO.NET support.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Exception Translation - Describes the features of Spring's data
|
|
access exceptions</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Parameter Management - Convenience classes and methods for easy
|
|
parameter management.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Custom IDataReader implementations - Strategy for providing
|
|
custom implementations of IDataReader. This can be used to centralized
|
|
and transparently map DBNull values to CLR types when accessing an
|
|
IDataReader or to provide extended mapping functionality in
|
|
sub-interfaces.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Basic data access operations - Usage of AdoTemplate for
|
|
IDbCommand 'ExecuteScalar' and 'ExecuteNonScalar' functionality</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Queries and Lightweight Object Mapping - Using AdoTemplate to
|
|
map result sets into objects</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>DataSet and DataTable operations - Using AdoTemplate with
|
|
DataSets and DataTables</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Modeling ADO.NET operations as .NET objects - An object-oriented
|
|
approach to data access operations.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="ado-motivations">
|
|
<title>Motivations</title>
|
|
|
|
<para>There are a variety of motivations to create a higher level ADO.NET
|
|
persistence API.</para>
|
|
|
|
<para>Encapsulation of common 'boiler plate' tasks when coding directly
|
|
against the ADO.NET API. For example here is a list of the tasks typically
|
|
required to be coded for processing a result set query. Note that the code
|
|
needed when using Spring's ADO.NET framework is in italics.</para>
|
|
|
|
<orderedlist numeration="arabic">
|
|
<listitem>
|
|
<para>Define connection parameters</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Open the connection</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><emphasis>Specify the command type and text</emphasis></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Prepare and execute the statement</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Set up the loop to iterate through the results (if any)</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><emphasis>Do the work for each iteration</emphasis></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Process any exception</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Display or rollback on warnings</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Handle transactions</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Close the connection</para>
|
|
</listitem>
|
|
</orderedlist>
|
|
|
|
<para>Spring takes care of the low-level tasks and lets you focus on
|
|
specifying the SQL and doing the real work of extracting data. This
|
|
standard boiler plate pattern is encapsulated in a class, AdoTemplate. The
|
|
name 'Template' is used because if you look at the typical code workflow
|
|
for the above listing, you would essentially like to 'template' it, that
|
|
is stick in the code that is doing the real work in the midst of the
|
|
resource, transaction, exception management.</para>
|
|
|
|
<para>Another very important motivation is to provide an easy means to
|
|
group multiple ADO.NET operations within a single transaction while at the
|
|
same time adhering to a DAO style design in which transactions are
|
|
initiated outside the DAOs, typically in a business service layer. Using
|
|
the 'raw' ADO.NET API to implement this design often results in explicitly
|
|
passing around of a Transaction/Connection pair to DAO objects. This
|
|
infrastructure task distracts from the main database task at hand and is
|
|
frequently done in an ad-hoc manner. Integrating with Spring's transaction
|
|
management features provides an elegant means to achieve this common
|
|
design goal. There are many other benefits to integration with Spring's
|
|
transaction management features, see <xref linkend="transaction" /> for
|
|
more information.</para>
|
|
|
|
<para>Provider Independent Code: In .NET 1.1 writing provider independent
|
|
code was difficult for a variety of reasons. The most prominent was the
|
|
lack of a lack of a central factory for creating interface based
|
|
references to the core ADO.NET classes such as IDbConnection, IDbCommand,
|
|
DbParameter etc. In addition, the APIs exposed by many of these interfaces
|
|
were minimal or incomplete - making for tedious code that would otherwise
|
|
be more easily developed with provider specific subclasses. Lastly, there
|
|
was no common base class for data access exceptions across the providers.
|
|
.NET 2.0 made many changes for the better in that regard across all these
|
|
areas of concern - and Spring only plugs smaller holes in that regard to
|
|
help in the portability of your data access code.</para>
|
|
|
|
<para>Resource Management: The 'using' block is the heart of elegant
|
|
resource management in .NET from the API perspective. However, despite its
|
|
elegance, writing 2-3 nested using statements for each data access method
|
|
also starts to be tedious, which introduces the risk of forgetting to do
|
|
the right thing <emphasis>all the time</emphasis> in terms of both direct
|
|
coding and 'cut-n-paste' errors. Spring centralizes this resource
|
|
management in one spot so you never forget or make a mistake and rely on
|
|
it always being done correctly.</para>
|
|
|
|
<para>Parameter management: Frequently much of data access code is related
|
|
to creating appropriate parameters. To alleviate this boiler plate code
|
|
Spring provides a parameter 'builder' class that allows for succinct
|
|
creation of parameter collections. Also, for the case of stored
|
|
procedures, parameters can be derived from the database itself which
|
|
reduces parameter creation code to just one line.</para>
|
|
|
|
<para>Frequently result set data is converted into objects. Spring
|
|
provides a simple framework to organize that mapping task and allows you
|
|
to reuse mapping artifacts across your application.</para>
|
|
|
|
<para>Exceptions: The standard course of action when an exception is
|
|
thrown from ADO.NET code is to look up the error code and then re-run the
|
|
application to set a break point where the exception occurred so as to see
|
|
what the command text and data values were that caused the exception.
|
|
Spring provides exceptions translation from these error codes (across
|
|
database vendors) to a Data Access Object exception hierarchy. This allows
|
|
you to quickly understand the category of the error that occurred and also
|
|
the 'bad' data which lead to the exception.</para>
|
|
|
|
<para>Warnings: A common means to extract warning from the database, and
|
|
to optionally treat those warnings as a reason to rollback is not directly
|
|
supported with the new System.Data.Common API</para>
|
|
|
|
<para>Portability: Where possible, increase the portability of code across
|
|
database provider in the higher level API. The need adding of a parameter
|
|
prefix, i.e. @ for SqlServer or ':' for oracle is one such example of an
|
|
area where a higher level API can offer some help in making your code more
|
|
portable.</para>
|
|
|
|
<para>Note that Spring's ADO.NET framework is just 'slightly' above the
|
|
raw API. It does not try to compete with other higher level persistence
|
|
abstractions such as result set mappers (iBATIS.NET) or other ORM tools
|
|
(NHibernate). (Apologies if your favorite is left out of that short list).
|
|
As always, pick and choose the appropriate level of abstraction for the
|
|
task at hand. As a side note, Spring does offer integration with higher
|
|
level persistence abstractions (currently NHibernate) providing such
|
|
features as integration with Spring's transaction management features as
|
|
well as mixing orm/ado.net operations within the same transaction.</para>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="ado-providers">
|
|
<title>Provider Abstraction</title>
|
|
|
|
<para>Before you get started executing queries against the database you
|
|
need to connect to it. <xref linkend="dbprovider" /> covers this topic in
|
|
detail so we only discuss the basic idea of how to interact with the
|
|
database in this section. One important ingredient that increases the
|
|
portability of writing ADO.NET applications is to refer to the base
|
|
ADO.NET interfaces, such as IDbCommand or IDbParameter in your code.
|
|
However, In the .NET 1.1 BCL the only means to obtain references to
|
|
instances of these interfaces is to directly instantiate the classes, i.e.
|
|
for SqlServer this would be <programlisting language="csharp"> IDbCommand command = new SqlCommand();</programlisting>
|
|
One of the classic creational patterns in the GoF Design Patterns book
|
|
addresses this situation directly, the Abstract Factory pattern. This
|
|
approach was applied in the .NET BCL with the introduction of the
|
|
DbProviderFactory class which contains various factory methods that create
|
|
the various objects used in ADO.NET programming. In addition, .NET 2.0
|
|
introduced new abstract base classes that all ADO.NET providers must
|
|
inherit from. These base classes provide more core functionality and
|
|
uniformity across the various providers as compared to the original
|
|
ADO.NET interfaces.</para>
|
|
|
|
<para>Spring's database provider abstraction has a similar API to that of
|
|
.ADO.NET 2.0's DbProviderFactory. The central interface is IDbProvider and
|
|
it has factory methods that are analogous to those in the
|
|
DbProviderFactory class except that they return references to the base
|
|
ADO.NET interfaces. Note that in keeping with the Spring Framework's
|
|
philosophy, IDbProvider is an interface, and can thus be easily mocked or
|
|
stubbed as necessary. Another key element of this interface is the
|
|
ConnectionString property that specifies the specific runtime information
|
|
necessary to connect to the provider. The interface also has a IDbMetadata
|
|
property that contains minimal database metadata information needed to
|
|
support the functionality in rest of the Spring ADO.NET framework. It is
|
|
unlikely you will need to use the DatabaseMetadata class directly in your
|
|
application.</para>
|
|
|
|
<para>For more information on configuring a Spring database provider refer
|
|
to <xref linkend="dbprovider" /></para>
|
|
|
|
<sect2 xml:id="ado-dbprovider-creating-instance">
|
|
<title>Creating an instance of IDbProvider</title>
|
|
|
|
<para>Each database vendor is associated with a particular
|
|
implementation of the IDbProvider interfaces. A variety of
|
|
implementations are provided with Spring such as SqlServer, Oracle and
|
|
MySql. Refer to the documentation on Spring's DbProvider for creating a
|
|
configuration for database that is not yet provided. The programmatic
|
|
way to create an IDbProvider is shown below</para>
|
|
|
|
<programlisting language="csharp">IDbProvider dbProvider = DbProviderFactory.GetDbProvider("System.Data.SqlClient");</programlisting>
|
|
</sect2>
|
|
|
|
<para>Please refer to the <xref linkend="dbprovider" /> for information on
|
|
how to create a IDbProvider in Spring's XML configuration file.</para>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="ado-namespaces">
|
|
<title>Namespaces</title>
|
|
|
|
<para>The ADO.NET framework consists of a few namespaces, namely
|
|
<package>Spring.Data</package>, <package>Spring.Data.Generic</package>,
|
|
<package>Spring.Data.Common</package>,
|
|
<package>Spring.Data.Support</package>, and
|
|
<package>Spring.Data.Object</package>.</para>
|
|
|
|
<para>The <package>Spring.Data</package> namespace contains the majority
|
|
of the classes and interfaces you will deal with on a day to day
|
|
basis.</para>
|
|
|
|
<para>The <package>Spring.Data.Generic</package> namespaces add generic
|
|
versions of some classes and interfaces and you will also likely deal with
|
|
this on a day to day basis if you are using .NET 2.0</para>
|
|
|
|
<para>The <package>Spring.Data.Common</package> namespaces contains
|
|
Spring's DbProvider abstraction in addition to utility classes for
|
|
parameter creation.</para>
|
|
|
|
<para>The <package>Spring.Data.Object</package> namespaces contains
|
|
classes that represent RDBMS queries, updates, and stored procedures as
|
|
thread safe, reusable objects.</para>
|
|
|
|
<para>Finally the <literal>Spring.Data.Support</literal> namespace is
|
|
where you find the <literal>IAdoExceptionTransactor</literal> translation
|
|
functionality and some utility classes.</para>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="ado-data-access-approaches">
|
|
<title>Approaches to Data Access</title>
|
|
|
|
<para>Spring provides two styles to interact with ADO.NET. The first is a
|
|
'template' based approach in which you create an single instance of
|
|
<literal>AdoTemplate</literal> to be used by all your DAO implementations.
|
|
Your DAO methods are frequently implemented as a single method call on the
|
|
template class as described in detail in the following section. The other
|
|
approach a more object-oriented manner that models database operations as
|
|
objects. For example, one can encapsulate the functionality of a database
|
|
query via an <literal>AdoQuery</literal> class and a create/update/delete
|
|
operation as a <literal>AdoNonQuery</literal> class. Stored procedures are
|
|
also modelled in this manner via the class
|
|
<literal>StoredProcedure</literal>. To use these classes you inherit from
|
|
them and define the details of the operation in the constructor and
|
|
implement an abstract method. This reads very cleanly when looking at DAO
|
|
method implementation as you can generally see all the details of what is
|
|
going on.</para>
|
|
|
|
<para>Generally speaking, experience has shown that the AdoTemplate
|
|
approach reads very cleanly when looking at DAO method implementation as
|
|
you can generally see all the details of what is going on as compared to
|
|
the object based approach. The object based approach however, offers some
|
|
advantages when calling stored procedures since it acts as a cache of
|
|
derived stored procedure arguments and can be invoked passing a variable
|
|
length argument list to the 'execute' method. As always, take a look at
|
|
both approaches and use the approach that provides you with the most
|
|
benefit for a particular situation.</para>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="ado-adotemplate-intro">
|
|
<title>Introduction to AdoTemplate</title>
|
|
|
|
<para>The class <literal>AdoTemplate</literal> is at the heart of Spring's
|
|
ADO.NET support. It is based on an Inversion of Control (i.e. callback)
|
|
design with the central method '<literal>Execute</literal>' handing you a
|
|
<literal>IDbCommand</literal> instance that has its Connection and
|
|
Transaction properties set based on the transaction context of the calling
|
|
code. All resource management is handled by the framework, you only need
|
|
to focus on dealing with the <literal>IDbCommand</literal> object. The
|
|
other methods in this class build upon this central 'Execute' method to
|
|
provide you a quick means to execute common data access scenarios.</para>
|
|
|
|
<para>There are two implementations of <literal>AdoTemplate</literal>. The
|
|
one that uses Generics and is in the namespace
|
|
<literal>Spring.Data.Generic</literal> and the other non-generic version
|
|
in <package>Spring.Data</package>. In either case you create an instance
|
|
of an <literal>AdoTemplate</literal> by passing it a
|
|
<literal>IDbProvider</literal> instance as shown below</para>
|
|
|
|
<programlisting language="csharp">AdoTemplate adoTemplate = new AdoTemplate(dbProvider);</programlisting>
|
|
|
|
<para><literal>AdoTemplate</literal> is a thread-safe class and as such a
|
|
single instance can be used for all data access operations in you
|
|
applications DAOs. <literal>AdoTemplate</literal> implements an
|
|
<literal>IAdoOperations</literal> interface. Although the
|
|
<literal>IAdoOperations</literal> interface is more commonly used for
|
|
testing scenarios you may prefer to code against it instead of the direct
|
|
class instance.</para>
|
|
|
|
<para>If you are using the generic version of AdoTemplate you can access
|
|
the non-generic version via the property ClassicAdoTemplate.</para>
|
|
|
|
<para>The following two sections show basic usage of the
|
|
<literal>AdoTemplate</literal> 'Execute' API for .NET 1.1 and 2.0.</para>
|
|
|
|
<para></para>
|
|
|
|
<sect2 xml:id="ado-execute-callback">
|
|
<title>Execute Callback</title>
|
|
|
|
<para>The <methodname>Execute</methodname> method and its associated
|
|
callback function/inteface is the basic method upon which all the other
|
|
methods in <literal>AdoTemplate</literal> delegate their work. If you
|
|
can not find a suitable 'one-liner' method in
|
|
<literal>AdoTemplate</literal> for your purpose you can always fall back
|
|
to the <methodname>Execute</methodname> method to perform any database
|
|
operation while benefiting from ADO.NET resource management and
|
|
transaction enlistment. This is commonly the case when you are using
|
|
special provider specific features, such as XML or BLOB support.</para>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="ado-execute-callback-net2">
|
|
<title>Execute Callback in .NET 2.0</title>
|
|
|
|
<para>In this example a simple query against the 'Northwind' database is
|
|
done to determine the number of customers who have a particular postal
|
|
code.</para>
|
|
|
|
<para><programlisting language="csharp">public int FindCountWithPostalCode(string postalCode)
|
|
{
|
|
return adoTemplate.Execute<int>(delegate(DbCommand command)
|
|
{
|
|
command.CommandText =
|
|
"select count(*) from Customers where PostalCode = @PostalCode";
|
|
|
|
DbParameter p = command.CreateParameter();
|
|
p.ParameterName = "@PostalCode";
|
|
p.Value = postalCode;
|
|
command.Parameters.Add(p);
|
|
|
|
return (int)command.ExecuteScalar();
|
|
|
|
});
|
|
|
|
}</programlisting>The <literal>DbCommand</literal> that is passed into the
|
|
anonymous delegate is already has it Connection property set to the
|
|
corresponding value of the dbProvider instance used to create the
|
|
template. Furthermore, the <methodname>Transaction</methodname> property
|
|
of the <literal>DbCommand</literal> is set based on the transactional
|
|
calling context of the code as based on the use of Spring's transaction
|
|
management features. Also note the feature of anonymous delegates to
|
|
access the variable 'postalCode' which is defined 'outside' the
|
|
anonymous delegate implementation. The use of anonymous delegates is a
|
|
powerful approach since it allows you to write compact data access code.
|
|
If you find that your callback implementation is getting very long, it
|
|
may improve code clarity to use an interface based version of the
|
|
callback function, i.e. an <literal>ICommandCallback</literal> shown
|
|
below.</para>
|
|
|
|
<para>As you can see, only the most relevant portions of the data access
|
|
task at hand need to be coded. (Note that in this simple example you
|
|
would be better off using AdoTemplate's ExecuteScalar method directly.
|
|
This method is described in the following sections). As mentioned
|
|
before, the typical usage scenario for the Execute callback would
|
|
involve downcasting the passed in <literal>DbCommand</literal> object to
|
|
access specific provider API features.</para>
|
|
|
|
<para>There is also an interface based version of the execute method.
|
|
The signatures for the delegate and interface are shown below</para>
|
|
|
|
<programlisting language="csharp">public delegate T CommandDelegate<T>(DbCommand command);
|
|
|
|
|
|
public interface ICommandCallback
|
|
{
|
|
T DoInCommand<T>(DbCommand command);
|
|
}</programlisting>
|
|
|
|
<para>While the delegate version offers the most compact syntax, the
|
|
interface version allows for reuse. The corresponding method signatures
|
|
on <package>Spring.Data.Generic.AdoTemplate</package> are shown
|
|
below</para>
|
|
|
|
<programlisting language="csharp">public class AdoTemplate : AdoAccessor, IAdoOperations
|
|
{
|
|
...
|
|
|
|
T Execute<T>(ICommandCallback action);
|
|
|
|
T Execute<T>(CommandDelegate<T> del);
|
|
|
|
...
|
|
}</programlisting>
|
|
|
|
<para>While it is common for .NET 2.0 ADO.NET provider implementations
|
|
to inherit from the base class System.Data.Common.DbCommand, that is not
|
|
a requirement. To accommodate the few that don't, which as of this
|
|
writing are the latest Oracle (ODP) provider, Postgres, and DB2 for
|
|
iSeries, two additional execute methods are provided. The only
|
|
difference is the use of callback and delegate implementations that have
|
|
IDbCommand and not DbCommand as callback arguments. The following
|
|
listing shows these methods on AdoTemplate.</para>
|
|
|
|
<programlisting language="csharp">public class AdoTemplate : AdoAccessor, IAdoOperations
|
|
{
|
|
...
|
|
|
|
T Execute<T>(IDbCommandCallback action);
|
|
|
|
T Execute<T>(IDbCommandDelegate<T> del);
|
|
|
|
...
|
|
}</programlisting>
|
|
|
|
<para>where the signatures for the delegate and interface are shown
|
|
below</para>
|
|
|
|
<programlisting language="csharp">public delegate T IDbCommandDelegate<T>(IDbCommand command);
|
|
|
|
|
|
public interface IDbCommandCallback<T>
|
|
{
|
|
T DoInCommand(IDbCommand command);
|
|
}</programlisting>
|
|
|
|
<para>Internally the <literal>AdoTemplate</literal> implementation
|
|
delegates to implementations of <literal>IDbCommandCallback</literal> so
|
|
that the 'lowest common denominator' API is used to have maximum
|
|
portability. If you accidentally call
|
|
<literal>Execute<T>(ICommandCallback action)</literal>and the
|
|
command does not inherit from <literal>DbCommand</literal>, an
|
|
<literal>InvalidDataAccessApiUsageException</literal> will be
|
|
thrown.</para>
|
|
|
|
<para>Depending on how portable you would like your code to be, you can
|
|
choose among the two callback styles. The one based on
|
|
<literal>DbCommand</literal> has the advantage of access to the more
|
|
user friendly <literal>DbParameter</literal> class as compared to
|
|
<literal>IDbParameter</literal> obtained from
|
|
<literal>IDbCommand</literal>.</para>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="ado-execute-callback-net11">
|
|
>
|
|
|
|
<title>Execute Callback in .NET 1.1</title>
|
|
|
|
|
|
|
|
<para>AdoTemplate differs from its .NET 2.0 generic counterpart in that
|
|
it exposes the interface <literal>IDbCommand</literal> in its 'Execute'
|
|
callback methods and delegate as compared to the abstract base class
|
|
<literal>DbProvider</literal>. Also, since anonymous delegates are not
|
|
available in .NET 1.1, the typical usage pattern requires you to create
|
|
a explicitly delegate and/or class that implements the
|
|
<literal>ICommandCallback</literal> interface. Example code to query In
|
|
.NET 1.1 the 'Northwind' database is done to determine the number of
|
|
customers who have a particular postal code is shown below.</para>
|
|
|
|
|
|
|
|
<programlisting language="csharp">public virtual int FindCountWithPostalCode(string postalCode)
|
|
{
|
|
return (int) AdoTemplate.Execute(new PostalCodeCommandCallback(postalCode));
|
|
}</programlisting>
|
|
|
|
|
|
|
|
<para>and the callback implementation is</para>
|
|
|
|
|
|
|
|
<programlisting language="csharp">private class PostalCodeCommandCallback : ICommandCallback
|
|
{
|
|
private string cmdText = "select count(*) from Customer where PostalCode = @PostalCode";
|
|
|
|
private string postalCode;
|
|
|
|
public PostalCodeCommandCallback(string postalCode)
|
|
{
|
|
this.postalCode = postalCode;
|
|
}
|
|
|
|
public object DoInCommand(IDbCommand command)
|
|
{
|
|
command.CommandText = cmdText;
|
|
|
|
IDbDataParameter p = command.CreateParameter();
|
|
p.ParameterName = "@PostalCode";
|
|
p.Value = postalCode;
|
|
command.Parameters.Add(p);
|
|
|
|
return command.ExecuteScalar();
|
|
|
|
}
|
|
}</programlisting>
|
|
|
|
|
|
|
|
<para>Note that in this example, one could more easily use AdoTemplate's
|
|
ExecuteScalar method.</para>
|
|
|
|
|
|
|
|
<para>The Execute method has interface and delegate overloads. The
|
|
signatures for the delegate and interface are shown below</para>
|
|
|
|
|
|
|
|
<programlisting language="csharp">public delegate object CommandDelegate(IDbCommand command);
|
|
|
|
public interface ICommandCallback
|
|
{
|
|
object DoInCommand(IDbCommand command);
|
|
}</programlisting>
|
|
|
|
|
|
|
|
<para>The corresponding method signatures on
|
|
<package>Spring.Data.AdoTemplate</package> are shown below</para>
|
|
|
|
|
|
|
|
<programlisting language="csharp">public class AdoTemplate : AdoAccessor, IAdoOperations
|
|
{
|
|
...
|
|
|
|
object Execute(CommandDelegate del);
|
|
|
|
object Execute(ICommandCallback action);
|
|
|
|
...
|
|
|
|
}</programlisting>
|
|
|
|
|
|
|
|
<para>Note that you have to cast to the appropriate object type returned
|
|
from the execute method.</para>
|
|
|
|
|
|
</sect2>
|
|
|
|
<sect2 xml:id="ado-template-method-guide">
|
|
<title>Quick Guide to AdoTemplate Methods</title>
|
|
|
|
<para>There are many methods in AdoTemplate so it is easy to feel a bit
|
|
overwhelmed when taking a look at the SDK documentation. However, after
|
|
a while you will hopefully find the class 'easy to navigate' with
|
|
intellisense. Here is a quick categorization of the method names and
|
|
their associated data access operation. Each method is overloaded to
|
|
handle common cases of passing in parameter values.</para>
|
|
|
|
<para>The generic 'catch-all' method</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><methodname>Execute</methodname> - Allows you to perform any
|
|
data access operation on a standard DbCommand object. The connection
|
|
and transaction properties of the DbCommand are already set based on
|
|
the transactional calling context. There is also an overloaded
|
|
method that operates on a standard IDbCommand object. This is for
|
|
those providers that do not inherit from the base class
|
|
DbCommand.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>The following methods mirror those on the DbCommand object.</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><methodname>ExecuteNonQuery</methodname> - Executes the
|
|
'NonQuery' method on a DbCommand, applying provided parameters and
|
|
returning the number of rows affected.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><methodname>ExecuteScalar</methodname> - Executes the 'Scalar'
|
|
method on a DbCommand, applying provided parameters, and returning
|
|
the first column of the first row in the result set.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>Mapping result sets to objects</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><methodname>QueryWithResultSetExtractor</methodname> - Execute
|
|
a query mapping a result set to an object with an implementation of
|
|
the <literal>IResultSetExtractor</literal> interface.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><methodname>QueryWithResultSetExtractorDelegate</methodname> -
|
|
Same as QueryWithResultSetExtractor but using a
|
|
<literal>ResultSetExtractorDelegate</literal> to perform result set
|
|
mapping.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><methodname>QueryWithRowCallback</methodname> - Execute a
|
|
query calling an implementation of <literal>IRowCallback</literal>
|
|
for each row in the result set.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><methodname>QueryWithRowCallbackDelegate</methodname> - Same
|
|
as QueryWithRowCallback but calling a
|
|
<literal>RowCallbackDelegate</literal> for each row.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><methodname>QueryWithRowMapper</methodname> - Execute a query
|
|
mapping a result set on a row by row basis with an implementation of
|
|
the <literal>IRowMapper</literal> interface.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><methodname>QueryWithRowMapperDelegate</methodname> - Same as
|
|
QueryWithRowMapper but using a <literal>RowMapperDelegate</literal>
|
|
to perform result set row to object mapping.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>Mapping result set to a single object</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><methodname>QueryForObject</methodname> - Execute a query
|
|
mapping the result set to an object using a
|
|
<literal>IRowMapper</literal>. Exception is thrown if the query does
|
|
not return exactly one object.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>Query with a callback to create the DbCommand object. These are
|
|
generally used by the framework itself to support other functionality,
|
|
such as in the Spring.Data.Objects namespace.</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><methodname>QueryWithCommandCreator</methodname> - Execute a
|
|
query with a callback to <literal>IDbCommandCreator</literal> to
|
|
create a IDbCommand object and using either a IRowMapper or
|
|
IResultSetExtractor to map the result set to an object. One
|
|
variation lets multiple result set 'processors' be specified to act
|
|
on multiple result sets and return output parameters.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>DataTable and DataSet operations</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><methodname>DataTableCreate</methodname> - Create and Fill
|
|
DataTables</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><methodname>DataTableCreateWithParameters</methodname> -
|
|
Create and Fill DataTables using a parameter collection.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><methodname>DataTableFill</methodname> - Fill a pre-existing
|
|
DataTable.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><methodname>DataTableFillWithParameters</methodname> - Fill a
|
|
pre-existing DataTable using parameter collection.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><methodname>DataTableUpdate</methodname> - Update the database
|
|
using the provided DataTable, insert, update, delete SQL.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><methodname>DataTableUpdateWithCommandBuilder</methodname> -
|
|
Update the database using the provided DataTable, select SQL, and
|
|
parameters.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><methodname>DataSetCreate</methodname> - Create and Fill
|
|
DataSets</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><methodname>DataSetCreateWithParameters</methodname> - Create
|
|
and Fill DataTables using a parameter collection.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><methodname>DataSetFill</methodname> - Fill a pre-existing
|
|
DataSet</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><methodname>DataSetFillWithParameters</methodname> - Fill a
|
|
pre-existing DataTable using parameter collection.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><methodname>DataSetUpdate</methodname> - Update the database
|
|
using the provided DataSet, insert, update, delete SQL.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><methodname>DataSetUpdateWithCommandBuilder</methodname> -
|
|
Update the database using the provided DataSet, select SQL, and
|
|
parameters..</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<note>
|
|
<para>These methods are not currently in the generic version of
|
|
AdoTemplate but accessible through the property
|
|
ClassicAdoTemplate.</para>
|
|
</note>
|
|
|
|
<para>Parameter Creation utility methods</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><methodname>DeriveParameters</methodname> - Derive the
|
|
parameter collection for stored procedures.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>In turn each method typically has four overloads, one with no
|
|
parameters and three for providing parameters. Aside from the
|
|
DataTable/DataSet operations, the three parameter overloads are of the
|
|
form shown below</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><literal>MethodName</literal>(CommandType cmdType, string
|
|
cmdText, <emphasis>CallbackInterfaceOrDelegate</emphasis>,
|
|
<emphasis>parameter setting arguments</emphasis>)</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>The CallbackInterfaceOrDelegate is one of the three types listed
|
|
previously. The parameters setting arguments are of the form</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><literal>MethodName( ... string parameterName, Enum dbType,
|
|
int size, object parameterValue</literal>)</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>MethodName( ... IDbParameters
|
|
parameters)</literal></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>MethodName( ... ICommandSetter
|
|
commandSetter)</literal></para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>The first overload is a convenience method when you only have one
|
|
parameter to set. The database enumeration is the base class 'Enum'
|
|
allowing you to pass in any of the provider specific enumerations as
|
|
well as the common DbType enumeration. This is a trade off of
|
|
type-safety with provider portability. (Note generic version could be
|
|
improved to provide type safety...).</para>
|
|
|
|
<para>The second overload contains a collection of parameters. The data
|
|
type is Spring's IDbParameters collection class discussed in the
|
|
following section.</para>
|
|
|
|
<para>The third overload is a callback interface allowing you to set the
|
|
parameters (or other properties) of the IDbCommand passed to you by the
|
|
framework directly.</para>
|
|
|
|
<para>If you are using .NET 2.0 the delegate versions of the methods are
|
|
very useful since very compact definitions of database operations can be
|
|
created that reference variables local to the DAO method. This removes
|
|
some of the tedium in passing parameters around with interface based
|
|
versions of the callback functions since they need to be passed into the
|
|
constructor of the implementing class. The general guideline is to use
|
|
the delegate when available for functionality that does not need to be
|
|
shared across multiple DAO classes or methods and use interface based
|
|
version to reuse the implementation in multiple places. The .NET 2.0
|
|
versions make use of generics where appropriate and therefore enhance
|
|
type-safety.</para>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="ado-adotemplate-properties-quicguide">
|
|
<title>Quick Guide to AdoTemplate Properties</title>
|
|
|
|
<para>AdoTemplate has the following properties that you can
|
|
configure</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><literal>LazyInit</literal> - Indicates if the
|
|
<literal>IAdoExceptionTranslator</literal> should be created on
|
|
first encounter of an exception from the data provider or when
|
|
<literal>AdoTemplate</literal> is created. Default is true, i.e. to
|
|
lazily instantiate.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>ExceptionTranslator</literal> - Gets or sets the
|
|
implementation of <literal>IAdoExceptionTranslator</literal> to use.
|
|
If no custom translator is provided, a default
|
|
<literal>ErrorCodeExceptionTranslator</literal> is used.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>DbProvider</literal> - Gets or sets the
|
|
<literal>IDbProvider</literal> instance to use.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>DataReaderWrapperType</literal> - Gets or set the
|
|
System.Type to use to create an instance of
|
|
<literal>IDataReaderWrapper</literal> for the purpose of providing
|
|
extended mapping functionality. Spring provides an implementation to
|
|
use as the basis for a mapping strategy that will map
|
|
<literal>DBNull</literal> values to default values based on the
|
|
standard <literal>IDataReader</literal> interface. See the section
|
|
<link linkend="ado-dbnull">custom IDataReader implementations</link>
|
|
for more information.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>CommandTimeout</literal> - Gets or sets the command
|
|
timeout for IDbCommands that this <literal>AdoTemplate</literal>
|
|
executes. Default is 0, indicating to use the database provider's
|
|
default.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</sect2>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="ado-transaction-management">
|
|
<title>Transaction Management</title>
|
|
|
|
<para>The AdoTemplate is used in conjunction with an implementation of a
|
|
<literal>IPlatformTransactionManager</literal>, which is Spring's portable
|
|
transaction management API. This section gives a brief overview of the
|
|
transaction managers you can use with AdoTemplate and the details of how
|
|
you can retrieve the connection/transaction ADO.NET objects that are bound
|
|
to the thread when a transaction starts. Please refer to the section <link
|
|
linkend="key-abstractions">key abstractions</link> in the chapter on
|
|
transactions for more comprehensive introduction to transaction
|
|
management.</para>
|
|
|
|
<para>To use local transactions, those with only one transactional
|
|
resource (i.e. the database) you will typically use
|
|
<literal>AdoPlatformTransactionManager</literal>. If you need to mix
|
|
Hibernate and ADO.NET data access operations within the same local
|
|
transaction you should use <literal>HibernatePlatformTransaction</literal>
|
|
manager which is described more in the section on <link
|
|
linkend="orm-tx-mgmt">ORM transaction management</link>.</para>
|
|
|
|
<para>While it is most common to use Spring's <link
|
|
linkend="transaction">transaction management features</link> to avoid the
|
|
low level management of ADO.NET connection and transaction objects, you
|
|
can retrieve the connection/transaction pair that was created at the start
|
|
of a transaction and bound to the current thread. This may be useful for
|
|
some integration with other data access APIs. The can be done using the
|
|
utility class ConnectionUtils as shown below.</para>
|
|
|
|
<programlisting language="csharp">IDbProvider dbProvider = DbProviderFactory.GetDbProvider("System.Data.SqlClient");
|
|
ConnectionTxPair connectionTxPairToUse = ConnectionUtils.GetConnectionTxPair(dbProvider);
|
|
|
|
|
|
IDbCommand command = DbProvider.CreateCommand();
|
|
command.Connection = connectionTxPairToUse.Connection;
|
|
command.Transaction = connectionTxPairToUse.Transaction;</programlisting>
|
|
|
|
<para>It is possible to provide a wrapper around the standard .NET
|
|
provider interfaces such that you can use the plain ADO.NET API in
|
|
conjunction with Spring's transaction management features.</para>
|
|
|
|
<para>If you are using
|
|
<literal>ServiceDomainPlatformTransactionManager</literal> or
|
|
<literal>TxScopePlatformTransactionManager</literal> then you can retrieve
|
|
the currently executing transaction object via the standard .NET
|
|
APIs.</para>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="ado-exception-translation">
|
|
<title>Exception Translation</title>
|
|
|
|
<para>AdoTemplate's methods throw exceptions within a Data Access Object
|
|
(DAO) exception hierarchy described in <xref linkend="dao" />. In
|
|
addition, the command text and error code of the exception are extracted
|
|
and logged. This leads to easier to write provider independent exception
|
|
handling layer since the exceptions thrown are not tied to a specific
|
|
persistence technology. Additionally, for ADO.NET code the error messages
|
|
logged provide information on the SQL and error code to better help
|
|
diagnose the issue.</para>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="ado-parameter-management">
|
|
<title>Parameter Management</title>
|
|
|
|
<para>A fair amount of the code in ADO.NET applications is related to the
|
|
creation and population of parameters. The BCL parameter interfaces are
|
|
very minimal and do not have many convenience methods found in provider
|
|
implementations such as SqlClient. Even still, with SqlClient, there is a
|
|
fair amount of verbosity to creating and populating a parameter
|
|
collection. Spring provides two ways to make this mundane task easier and
|
|
more portable across providers.</para>
|
|
|
|
<sect2 xml:id="ado-idbparametersbuilder">
|
|
<title>IDbParametersBuilder</title>
|
|
|
|
<para>Instead of creating a parameter on one line of code, then setting
|
|
its type on another and size on another, a builder and parameter
|
|
interface, <literal>IDbParametersBuilder</literal> and
|
|
<literal>IDbParameter</literal> respectfully, are provided so that this
|
|
declaration process can be condensed. The IDbParameter support chaining
|
|
calls to its methods, in effect a simple language-constrained domain
|
|
specific language, to be fancy about it. Here is an example of it in
|
|
use.</para>
|
|
|
|
<programlisting language="csharp">IDbParametersBuilder builder = CreateDbParametersBuilder();
|
|
builder.Create().Name("Country").Type(DbType.String).Size(15).Value(country);
|
|
builder.Create().Name("City").Type(DbType.String).Size(15).Value(city);
|
|
|
|
|
|
// now get the IDbParameters collection for use in passing to AdoTemplate methods.
|
|
|
|
|
|
IDbParameters parameters = builder.GetParameters();</programlisting>
|
|
|
|
<para>Please note that <literal>IDbParameters</literal> and
|
|
<literal>IDbParameter</literal> are not part of the BCL, but part of the
|
|
Spring.Data.Common namespace. The IDbParameters collection is a frequent
|
|
argument to the overloaded methods of AdoTemplate.</para>
|
|
|
|
<para>The parameter prefix, i.e. '@' in Sql Server, is not required to
|
|
be added to the parameter name. The DbProvider is aware of this metadata
|
|
and AdoTemplate will add it automatically if required before
|
|
execution.</para>
|
|
|
|
<para>An additional feature of the IDbParametersBuilder is to create a
|
|
Spring FactoryObject that creates IDbParameters for use in the XML
|
|
configuration file of the IoC container. By leveraging Spring's
|
|
expression evaluation language, the above lines of code can be taken as
|
|
text from the XML configuration file and executed. As a result you can
|
|
externalize your parameter definitions from your code. In combination
|
|
with abstract object definitions and importing of configuration files
|
|
your increase the chances of having one code base support multiple
|
|
database providers just by a change in configuration files.</para>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="ado-idbparameters">
|
|
<title>IDbParameters</title>
|
|
|
|
<para>This class is similar to the parameter collection class you find
|
|
in provider specific implementations of IDataParameterCollection. It
|
|
contains a variety of convenience methods to build up a collection of
|
|
parameters.</para>
|
|
|
|
<para>Here is an abbreviated listing of the common convenience
|
|
methods.</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>int Add(object parameterValue)</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>void AddRange(Array values)</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>IDbDataParameter AddWithValue(string name, object
|
|
parameterValue)</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>IDbDataParameter Add(string name, Enum parameterType)</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>IDbDataParameter AddOut(string name, Enum
|
|
parameterType)</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>IDbDataParameter AddReturn(string name, Enum
|
|
parameterType)</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>void DeriveParameters(string storedProcedureName)</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>Here a simple usage example</para>
|
|
|
|
<programlisting language="csharp">// inside method has has local variable country and city...
|
|
|
|
IDbParameters parameters = CreateDbParameters();
|
|
parameters.AddWithValue("Country", country).DbType = DbType.String;
|
|
parameters.Add("City", DbType.String).Value = city;
|
|
|
|
|
|
// now pass on to AdoTemplate methods.</programlisting>
|
|
|
|
<para>The parameter prefix, i.e. '@' in Sql Server, is not required to
|
|
be added to the parameter name. The DbProvider is aware of this metadata
|
|
and AdoTemplate will add it automatically if required before
|
|
execution.</para>
|
|
</sect2>
|
|
|
|
<sect2>
|
|
<title>Parameter names in SQL text</title>
|
|
|
|
<para>While the use of <classname>IDbParameters</classname> or
|
|
<classname>IDbParametersBuilder</classname> will remove the need for use
|
|
to vendor specific parameter prefixes when creating a parameter
|
|
collection, @User in Sql SqlSerer vs. :User in Oracle, you still need to
|
|
specify the vendor specific parameter prefix in the SQL Text. Portable
|
|
SQL in this regard is possible to implement, it is available as a
|
|
feature in Spring Java. If you would like such a feature, please <link
|
|
ns2:href="http://jira.springsource.org/secure/CreateIssue!default.jspa?pid=10020">raise
|
|
an issue</link>.</para>
|
|
</sect2>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="ado-dbnull">
|
|
<title>Custom IDataReader implementations</title>
|
|
|
|
<para>The passed in implementation of <literal>IDataReader</literal> can
|
|
be customized. This lets you add a strategy for handling null values to
|
|
the standard methods in the <literal>IDataReader</literal> interface or to
|
|
provide sub-interface of IDataReader that contains extended functionality,
|
|
for example support for default values. In callback code, i.e. IRowMapper
|
|
and associated delegate, you would downcast to the sub-interface to
|
|
perform processing.</para>
|
|
|
|
<para>Spring provides a class to map <literal>DBNull</literal> values to
|
|
default values. When reading from a IDataReader there is often the need to
|
|
map <literal>DBNull</literal> values to some default values, i.e. null or
|
|
say a magic number such as -1. This is usually done via a ternary operator
|
|
which decreases readability and also increases the likelihood of mistakes.
|
|
Spring provides an <literal>IDataReaderWrapper</literal> interface (which
|
|
inherits from the standard <literal>IDataReader</literal>) so that you can
|
|
provide your own implementation of a IDataReader that will perform DBNull
|
|
mapping for you in a consistent and non invasive manner to your result set
|
|
reading code. A default implementation,
|
|
<literal>NullMappingDataReader</literal> is provided which you can
|
|
subclass to customize or simply implement the
|
|
<literal>IDataReaderWrapper</literal> interface directly. This interface
|
|
is shown below</para>
|
|
|
|
<programlisting language="csharp"> public interface IDataReaderWrapper : IDataReader
|
|
{
|
|
IDataReader WrappedReader
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
}</programlisting>
|
|
|
|
<para>All of AdoTemplates callback interfaces/delegates that have an
|
|
<literal>IDataReader</literal> as an argument are wrapped with a
|
|
<literal>IDataReaderWrapper</literal> if the AdoTemplate has been
|
|
configured with one via its <methodname>DataReaderWrapperType</methodname>
|
|
property. Your implementation should support a zero-arg
|
|
constructor.</para>
|
|
|
|
<para>Frequently you will use a common mapper for DBNull across your
|
|
application so only one instance of <literal>AdoTemplate</literal> and
|
|
<literal>IDataReaderWrapper</literal> in required. If you need to use
|
|
multiple null mapping strategies you will need to create multiple
|
|
instances of <literal>AdoTemplate</literal> and configure them
|
|
appropriately in the DAO objects.</para>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="ado-basic-operations">
|
|
<title>Basic data access operations</title>
|
|
|
|
<para>The 'ExecuteNonQuery' and 'ExecuteScalar' methods of
|
|
<literal>AdoTemplate</literal> have the same functionality as the same
|
|
named methods on the DbCommand object</para>
|
|
|
|
<sect2 xml:id="ado-executenonquery">
|
|
<title>ExecuteNonQuery</title>
|
|
|
|
<para>ExecuteNonQuery is used to perform create, update, and delete
|
|
operations. It has four overloads listed below reflecting different ways
|
|
to set the parameters.</para>
|
|
|
|
<para>An example of using this method is shown below</para>
|
|
|
|
<para><programlisting language="csharp"> public void CreateCredit(float creditAmount)
|
|
{
|
|
AdoTemplate.ExecuteNonQuery(CommandType.Text,
|
|
String.Format("insert into Credits(creditAmount) VALUES ({0})",
|
|
creditAmount));
|
|
}</programlisting></para>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="ado-executescalar">
|
|
<title>ExecuteScalar</title>
|
|
|
|
<para>An example of using this method is shown below</para>
|
|
|
|
<para><programlisting language="csharp">int iCount = (int)adoTemplate.ExecuteScalar(CommandType.Text, "SELECT COUNT(*) FROM TestObjects");
|
|
</programlisting></para>
|
|
</sect2>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="ado-lightweight-orm">
|
|
<title>Queries and Lightweight Object Mapping</title>
|
|
|
|
<para>A common ADO.NET development task is reading in a result set and
|
|
converting it to a collection of domain objects. The family of QueryWith
|
|
methods on AdoTemplate help in this task. The responsibility of performing
|
|
the mapping is given to one of three callback interfaces/delegates that
|
|
you are responsible for developing. These callback interfaces/delegates
|
|
are:</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>IResultSetExtractor / ResultSetExtractorDelegate - hands you a
|
|
IDataReader object for you to iterate over and return a result
|
|
object.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>IRowCallback / RowCallbackDelegate - hands you a IDataReader to
|
|
process the current row. Returns void and as such is usually stateful
|
|
in the case of IRowCallback implementations or uses a variable to
|
|
collect a result that is available to an anonymous delegate.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>IRowMapper / RowMapperDelegate - hands you a IDataReader to
|
|
process the current row and return an object corresponding to that
|
|
row.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>There are generic versions of the IResultSetExtractor and IRowMapper
|
|
interfaces/delegates providing you with additional type-safety as compared
|
|
to the object based method signatures used in the .NET 1.1
|
|
implementation.</para>
|
|
|
|
<para>As usual with callback APIs in Spring.Data, your implementations of
|
|
these interfaces/delegates are only concerned with the core task at hand -
|
|
mapping data - while the framework handles iteration of readers and
|
|
resource management.</para>
|
|
|
|
<para>Each 'QueryWith' method has 4 overloads to handle common ways to
|
|
bind parameters to the command text.</para>
|
|
|
|
<para>The following sections describe in more detail how to use Spring's
|
|
lightweight object mapping framework.</para>
|
|
|
|
<sect2 xml:id="ado-resultsetextractor">
|
|
<title>ResultSetExtractor</title>
|
|
|
|
<para>The ResultSetExtractor gives you control to iterate over the
|
|
IDataReader returned from the query. You are responsible for iterating
|
|
through all the result sets and returning a corresponding result object.
|
|
Implementations of IResultSetExtractor are typically stateless and
|
|
therefore reusable as long as the implementation doesn't access stateful
|
|
resources. The framework will close the IDataReader for you.</para>
|
|
|
|
<para>The interface and delegate signature for ResutSetExtractors is
|
|
shown below for the generic version in the Spring.Data.Generic
|
|
namespace</para>
|
|
|
|
<programlisting language="csharp">public interface IResultSetExtractor<T>
|
|
{
|
|
T ExtractData(IDataReader reader);
|
|
}
|
|
|
|
public delegate T ResultSetExtractorDelegate<T>(IDataReader reader);
|
|
</programlisting>
|
|
|
|
<para>The definition for the non-generic version is shown below</para>
|
|
|
|
<programlisting language="csharp">public interface IResultSetExtractor
|
|
{
|
|
object ExtractData(IDataReader reader);
|
|
}
|
|
|
|
public delegate object ResultSetExtractorDelegate(IDataReader reader);</programlisting>
|
|
|
|
<para>Here is an example taken from the Spring.DataQuickStart. It is a
|
|
method in a DAO class that inherits from AdoDaoSupport, which has a
|
|
convenience method 'CreateDbParametersBuilder()'.</para>
|
|
|
|
<para></para>
|
|
|
|
<programlisting language="csharp"> public virtual IList<string> GetCustomerNameByCountryAndCityWithParamsBuilder(string country, string city)
|
|
{
|
|
|
|
IDbParametersBuilder builder = CreateDbParametersBuilder();
|
|
builder.Create().Name("Country").Type(DbType.String).Size(15).Value(country);
|
|
builder.Create().Name("City").Type(DbType.String).Size(15).Value(city);
|
|
return AdoTemplate.QueryWithResultSetExtractor(CommandType.Text,
|
|
customerByCountryAndCityCommandText,
|
|
new CustomerNameResultSetExtractor<List<string>>(),
|
|
builder.GetParameters());
|
|
}
|
|
</programlisting>
|
|
|
|
<para>The implementation of the ResultSetExtractor is shown
|
|
below.</para>
|
|
|
|
<programlisting language="csharp"> internal class CustomerNameResultSetExtractor<T> : IResultSetExtractor<T> where T : IList<string>, new()
|
|
{
|
|
|
|
public T ExtractData(IDataReader reader)
|
|
{
|
|
T customerList = new T();
|
|
while (reader.Read())
|
|
{
|
|
string contactName = reader.GetString(0);
|
|
customerList.Add(contactName);
|
|
}
|
|
return customerList;
|
|
}
|
|
}</programlisting>
|
|
|
|
<para>Internally the implementation of the QueryWithRowCallback and
|
|
QueryWithRowMapper methods are specializations of the general
|
|
ResultSetExtractor. For example, the QueryWithRowMapper implementation
|
|
iterates through the result set, calling the callback method 'MapRow'
|
|
for each row and collecting the results in an IList. If you have a
|
|
specific case that is not covered by the QueryWithXXX methods you can
|
|
subclass AdoTemplate and follow the same implementation pattern to
|
|
create a new QueryWithXXX method to suit your needs.</para>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="ado-rowcallback">
|
|
<title>RowCallback</title>
|
|
|
|
<para>The RowCallback is usually a stateful object itself or populates
|
|
another stateful object that is accessible to the calling code. Here is
|
|
a sample take from the Data QuickStart</para>
|
|
|
|
<programlisting language="csharp"> public class RowCallbackDao : AdoDaoSupport
|
|
{
|
|
private string cmdText = "select ContactName, PostalCode from Customers";
|
|
|
|
public virtual IDictionary<string, IList<string>> GetPostalCodeCustomerMapping()
|
|
{
|
|
PostalCodeRowCallback statefullCallback = new PostalCodeRowCallback();
|
|
AdoTemplate.QueryWithRowCallback(CommandType.Text, cmdText,
|
|
statefullCallback);
|
|
|
|
// Do something with results in stateful callback...
|
|
return statefullCallback.PostalCodeMultimap;
|
|
}
|
|
|
|
}</programlisting>
|
|
|
|
<para>The PostalCodeRowCallback builds up state which is then retrieved
|
|
via the property PostalCodeMultimap. The Callback implementation is
|
|
shown below</para>
|
|
|
|
<programlisting language="csharp"> internal class PostalCodeRowCallback : IRowCallback
|
|
{
|
|
private IDictionary<string, IList<string>> postalCodeMultimap =
|
|
new Dictionary<string, IList<string>>();
|
|
|
|
public IDictionary<string, IList<string>> PostalCodeMultimap
|
|
{
|
|
get { return postalCodeMultimap; }
|
|
}
|
|
|
|
public void ProcessRow(IDataReader reader)
|
|
{
|
|
string contactName = reader.GetString(0);
|
|
string postalCode = reader.GetString(1);
|
|
IList<string> contactNameList;
|
|
if (postalCodeMultimap.ContainsKey(postalCode))
|
|
{
|
|
contactNameList = postalCodeMultimap[postalCode];
|
|
}
|
|
else
|
|
{
|
|
postalCodeMultimap.Add(postalCode, contactNameList = new List<string>());
|
|
}
|
|
contactNameList.Add(contactName);
|
|
}
|
|
}</programlisting>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="ado-rowmapper">
|
|
<title>RowMapper</title>
|
|
|
|
<para>The RowMapper lets you focus on just the logic to map a row of
|
|
your result set to an object. The creation of a IList to store the
|
|
results and iterating through the IDataReader is handled by the
|
|
framework. Here is a simple example taken from the Data QuickStart
|
|
application</para>
|
|
|
|
<programlisting language="csharp"> public class RowMapperDao : AdoDaoSupport
|
|
{
|
|
private string cmdText = "select Address, City, CompanyName, ContactName, " +
|
|
"ContactTitle, Country, Fax, CustomerID, Phone, PostalCode, " +
|
|
"Region from Customers";
|
|
|
|
|
|
public virtual IList<Customer> GetCustomers()
|
|
{
|
|
return AdoTemplate.QueryWithRowMapper<Customer>(CommandType.Text, cmdText,
|
|
new CustomerRowMapper<Customer>());
|
|
}
|
|
}</programlisting>
|
|
|
|
<para>where the implementation of the RowMapper is</para>
|
|
|
|
<programlisting language="csharp"> public class CustomerRowMapper<T> : IRowMapper<T> where T : Customer, new()
|
|
{
|
|
public T MapRow(IDataReader dataReader, int rowNum)
|
|
{
|
|
T customer = new T();
|
|
customer.Address = dataReader.GetString(0);
|
|
customer.City = dataReader.GetString(1);
|
|
customer.CompanyName = dataReader.GetString(2);
|
|
customer.ContactName = dataReader.GetString(3);
|
|
customer.ContactTitle = dataReader.GetString(4);
|
|
customer.Country = dataReader.GetString(5);
|
|
customer.Fax = dataReader.GetString(6);
|
|
customer.Id = dataReader.GetString(7);
|
|
customer.Phone = dataReader.GetString(8);
|
|
customer.PostalCode = dataReader.GetString(9);
|
|
customer.Region = dataReader.GetString(10);
|
|
return customer;
|
|
}
|
|
}</programlisting>
|
|
|
|
<para>You may also pass in a delegate, which is particularly convenient
|
|
if the mapping logic is short and you need to access local variables
|
|
within the mapping logic.</para>
|
|
|
|
<programlisting language="csharp"> public virtual IList<Customer> GetCustomersWithDelegate()
|
|
{
|
|
return AdoTemplate.QueryWithRowMapperDelegate<Customer>(CommandType.Text, cmdText,
|
|
delegate(IDataReader dataReader, int rowNum)
|
|
{
|
|
Customer customer = new Customer();
|
|
customer.Address = dataReader.GetString(0);
|
|
customer.City = dataReader.GetString(1);
|
|
customer.CompanyName = dataReader.GetString(2);
|
|
customer.ContactName = dataReader.GetString(3);
|
|
customer.ContactTitle = dataReader.GetString(4);
|
|
customer.Country = dataReader.GetString(5);
|
|
customer.Fax = dataReader.GetString(6);
|
|
customer.Id = dataReader.GetString(7);
|
|
customer.Phone = dataReader.GetString(8);
|
|
customer.PostalCode = dataReader.GetString(9);
|
|
customer.Region = dataReader.GetString(10);
|
|
return customer;
|
|
});
|
|
}</programlisting>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="ado-query-for-single-object">
|
|
<title>Query for a single object</title>
|
|
|
|
<para>The QueryForObject method is used when you expect there to be
|
|
exactly one object returned from the mapping, otherwise a
|
|
Spring.Dao.IncorrectResultSizeDataAccessException will be thrown. Here
|
|
is some sample usage taken from the Data QuickStart.</para>
|
|
|
|
<programlisting language="csharp"> public class QueryForObjectDao : AdoDaoSupport
|
|
{
|
|
private string cmdText = "select Address, City, CompanyName, ContactName, " +
|
|
"ContactTitle, Country, Fax, CustomerID, Phone, PostalCode, " +
|
|
"Region from Customers where ContactName = @ContactName";
|
|
|
|
public Customer GetCustomer(string contactName)
|
|
{
|
|
return AdoTemplate.QueryForObject(CommandType.Text, cmdText,
|
|
new CustomerRowMapper<Customer>(),
|
|
"ContactName", DbType.String, 30, contactName);
|
|
}
|
|
}</programlisting>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="ado-queyr-commandcreator">
|
|
<title>Query using a CommandCreator</title>
|
|
|
|
<para>There is a family of overloaded methods that allows you to
|
|
encapsulate and reuse a particular configuration of a
|
|
<literal>IDbCommand</literal> object. These methods also allow for
|
|
access to returned out parameters as well as a method that allows
|
|
processing of multiple result sets. These methods are used internally to
|
|
support the classes in the <package>Spring.Data.Objects</package>
|
|
namespace and you may find the API used in that namespace to be more
|
|
convenient. The family of methods is listed below.</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><literal>object QueryWithCommandCreator(IDbCommandCreator cc,
|
|
IResultSetExtractor rse)</literal></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>void QueryWithCommandCreator(IDbCommandCreator cc,
|
|
IRowCallback rowCallback)</literal></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>IList QueryWithCommandCreator(IDbCommandCreator cc,
|
|
IRowMapper rowMapper)</literal></para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>There is also the same methods with an additional collecting
|
|
parameter to obtain any output parameters. These are</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><literal>object QueryWithCommandCreator(IDbCommandCreator cc,
|
|
IResultSetExtractor rse, IDictionary
|
|
returnedParameters)</literal></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>void QueryWithCommandCreator(IDbCommandCreator cc,
|
|
IRowCallback rowCallback, IDictionary
|
|
returnedParameters)</literal></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>IList QueryWithCommandCreator(IDbCommandCreator cc,
|
|
IRowMapper rowMapper, IDictionary
|
|
returnedParameters)</literal></para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>The IDbCommandCreator callback interface is shown below</para>
|
|
|
|
<programlisting language="csharp"> public interface IDbCommandCreator
|
|
{
|
|
IDbCommand CreateDbCommand();
|
|
}</programlisting>
|
|
|
|
<para>The created IDbCommand object is used when performing the
|
|
QueryWithCommandCreator method.</para>
|
|
|
|
<para>To process multiple result sets specify a list of named result set
|
|
processors,( i.e. <literal>IResultSetExtractor</literal>,
|
|
<literal>IRowCallback</literal>, or <literal>IRowMapper). </literal>This
|
|
method is shown below</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><literal>IDictionary QueryWithCommandCreator(IDbCommandCreator
|
|
cc, IList namedResultSetProcessors)</literal></para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>The list must contain objects of the type
|
|
<literal>Spring.Data.Support.NamedResultSetProcessor</literal>. This is
|
|
the class responsible for associating a name with a result set
|
|
processor. The constructors are listed below.</para>
|
|
|
|
<programlisting language="csharp">public class NamedResultSetProcessor {
|
|
|
|
public NamedResultSetProcessor(string name, IRowMapper rowMapper) { ... }
|
|
|
|
public NamedResultSetProcessor(string name, IRowCallback rowcallback) { ... }
|
|
|
|
public NamedResultSetProcessor(string name, IResultSetExtractor resultSetExtractor) { ... }
|
|
|
|
. . .
|
|
|
|
}</programlisting>
|
|
|
|
<para>The results of the RowMapper or ResultSetExtractor are retrieved
|
|
by name from the dictionary that is returned. RowCallbacks, being
|
|
stateless, only have the placeholder text, "ResultSet returned was
|
|
processed by an IRowCallback" as a value for the name of the RowCallback
|
|
used as a key. Output and InputOutput parameters can be retrieved by
|
|
name. If this parameter name is null, then the index of the parameter
|
|
prefixed with the letter 'P' is a key name, i.e P2, P3, etc.</para>
|
|
|
|
<para>The namespace Spring.Data.Objects.Generic contains generic
|
|
versions of these methods. These are listed below</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><literal>T QueryWithCommandCreator<T>(IDbCommandCreator
|
|
cc, IResultSetExtractor<T> rse)</literal></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>IList<T>
|
|
QueryWithCommandCreator<T>(IDbCommandCreator cc,
|
|
IRowMapper<T> rowMapper)</literal></para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>and overloads that have an additional collecting parameter to
|
|
obtain any output parameters.</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><literal>T QueryWithCommandCreator<T>(IDbCommandCreator
|
|
cc, IResultSetExtractor<T> rse, IDictionary
|
|
returnedParameters)</literal></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>IList<T>
|
|
QueryWithCommandCreator<T>(IDbCommandCreator cc,
|
|
IRowMapper<T> rowMapper, IDictionary
|
|
returnedParameters)</literal></para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>When processing multiple result sets you can specify up to two
|
|
type safe result set processors.</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><literal>IDictionary
|
|
QueryWithCommandCreator<T>(IDbCommandCreator cc, IList
|
|
namedResultSetProcessors)</literal></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>IDictionary
|
|
QueryWithCommandCreator<T,U>(IDbCommandCreator cc, IList
|
|
namedResultSetProcessors)</literal></para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>The list of result set processors contains either objects of the
|
|
type Spring.Data.Generic.NamedResultSetProcessor<T> or
|
|
Spring.Data.NamedResultSetProcessor. The generic result set processors,
|
|
NamedResultSetProcessor<T>, is used to process the first result
|
|
set in the case of using QueryWithCommandCreator<T> and to process
|
|
the first and second result set in the case of using
|
|
QueryWithCommandCreator<T,U>. Additional
|
|
Spring.Data.NamedResultSetProcessors that are listed can be used to
|
|
process additional result sets. If you specify a RowCallback with
|
|
NamedResultSetProcessor<T>, you still need to specify a type
|
|
parameter (say string) because the RowCallback processor does not return
|
|
any object. It is up to subclasses of RowCallback to collect state due
|
|
to processing the result set which is later queried.</para>
|
|
</sect2>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="ado-datatable-dataset">
|
|
<title>DataTable and DataSet</title>
|
|
|
|
<para>AdoTemplate contains several 'families' of methods to help remove
|
|
boilerplate code and reduce common programming errors when using
|
|
DataTables and DataSets. There are many methods in AdoTemplate so it is
|
|
easy to feel a bit overwhelmed when taking a look at the SDK
|
|
documentation. However, after a while you will hopefully find the class
|
|
'easy to navigate' with intellisense. Here is a quick categorization of
|
|
the method names and their associated data access operation. Each method
|
|
is overloaded to handle common cases of passing in parameter
|
|
values.</para>
|
|
|
|
<para>The 'catch-all' Execute methods upon which other functionality is
|
|
built up upon are shown below.</para>
|
|
|
|
<para>In <property>Spring.Data.Core.AdoTemplate</property></para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><literal>object Execute(IDataAdapterCallback
|
|
dataAdapterCallback)</literal> - Execute ADO.NET operations on a
|
|
IDbDataAdapter object using an interface based callback.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>Where <literal>IDataAdapterCallback</literal> is defined as</para>
|
|
|
|
<programlisting language="csharp">public interface IDataAdapterCallback
|
|
{
|
|
object DoInDataAdapter(IDbDataAdapter dataAdapter);
|
|
}</programlisting>
|
|
|
|
<para>The passed in <literal>IDbDataAdapter</literal> will have its
|
|
<property>SelectCommand</property> property created and set with its
|
|
<property>Connection</property> and <property>Transaction</property>
|
|
values based on the calling transaction context. The return value is the
|
|
result of processing or null.</para>
|
|
|
|
<para>There are type-safe versions of this method in
|
|
<literal>Spring.Data.Generic.AdoTemplate</literal></para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><literal>T Execute<T>(IDataAdapterCallback<T>
|
|
dataAdapterCallback) </literal>- Execute ADO.NET operations on a
|
|
IDbDataAdapter object using an interface based callback.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>T Execute<T>(DataAdapterDelegate<T> del)
|
|
</literal>- Execute ADO.NET operations on a IDbDataAdapter object
|
|
using an delegate based callback.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>Where IDataAdapterCallback<T> and DataAdapterDelegate<T>
|
|
are defined as</para>
|
|
|
|
<programlisting language="csharp">public interface IDataAdapterCallback<T>
|
|
{
|
|
T DoInDataAdapter(IDbDataAdapter dataAdapter);
|
|
}
|
|
|
|
public delegate T DataAdapterDelegate<T>(IDbDataAdapter dataAdapter);</programlisting>
|
|
|
|
<sect2 xml:id="ado-datatable">
|
|
<title>DataTables</title>
|
|
|
|
<para>DataTable operations are available on the class
|
|
<literal>Spring.Data.Core.AdoTemplate</literal>. If you are using the
|
|
generic version, <literal>Spring.Data.Generic.AdoTemplate</literal>, you
|
|
can access these methods through the property
|
|
<property>ClassicAdoTemplate</property>, which returns the non-generic
|
|
version of AdoTemplate. DataTable operations available fall into the
|
|
general family of methods with 3-5 overloads per method.</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><methodname>DataTableCreate</methodname> - Create and Fill
|
|
DataTables</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><methodname>DataTableCreateWithParameters</methodname> -
|
|
Create and Fill DataTables using a parameter collection.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><methodname>DataTableFill</methodname> - Fill a pre-existing
|
|
DataTable.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><methodname>DataTableFillWithParameters</methodname> - Fill a
|
|
pre-existing DataTable using a parameter collection.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><methodname>DataTableUpdate</methodname> - Update the database
|
|
using the provided DataTable, insert, update, delete SQL.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><methodname>DataTableUpdateWithCommandBuilder</methodname> -
|
|
Update the database using the provided DataTable, select SQL, and
|
|
parameters.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="ado-dataset">
|
|
<title>DataSets</title>
|
|
|
|
<para>DataSet operations are available on the class
|
|
<literal>Spring.Data.Core.AdoTemplate</literal>. If you are using the
|
|
generic version, <literal>Spring.Data.Generic.AdoTemplate</literal>, you
|
|
can access these methods through the property
|
|
<property>ClassicAdoTemplate</property>, which returns the non-generic
|
|
version of AdoTemplate. DataSet operations available fall into the
|
|
following family of methods with 3-5 overloads per method.</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><methodname>DataSetCreate</methodname> - Create and Fill
|
|
DataSets</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><methodname>DataSetCreateWithParameters</methodname> - Create
|
|
and Fill DataTables using a parameter collection.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><methodname>DataSetFill</methodname> - Fill a pre-existing
|
|
DataSet</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><methodname>DataSetFillWithParameters</methodname> - Fill a
|
|
pre-existing DataTable using parameter collection.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><methodname>DataSetUpdate</methodname> - Update the database
|
|
using the provided DataSet, insert, update, delete SQL.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><methodname>DataSetUpdateWithCommandBuilder</methodname> -
|
|
Update the database using the provided DataSet, select SQL, and
|
|
parameters.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>The following code snippets demonstrate the basic functionality of
|
|
these methods using the Northwind database. See the SDK documentation
|
|
for more details on other overloaded methods.</para>
|
|
|
|
<para><programlisting language="csharp">public class DataSetDemo : AdoDaoSupport
|
|
{
|
|
private string selectAll = @"select Address, City, CompanyName, ContactName, " +
|
|
"ContactTitle, Country, Fax, CustomerID, Phone, PostalCode, " +
|
|
"Region from Customers";
|
|
|
|
|
|
public void DemoDataSetCreate()
|
|
{
|
|
DataSet customerDataSet = AdoTemplate.DataSetCreate(CommandType.Text, selectAll);
|
|
|
|
// customerDataSet has a table named 'Table' with 91 rows
|
|
|
|
customerDataSet = AdoTemplate.DataSetCreate(CommandType.Text, selectAll, new string[] { "Customers" });
|
|
|
|
// customerDataSet has a table named 'Customers' with 91 rows
|
|
|
|
}
|
|
|
|
|
|
public void DemoDataSetCreateWithParameters()
|
|
{
|
|
string selectLike = @"select Address, City, CompanyName, ContactName, " +
|
|
"ContactTitle, Country, Fax, CustomerID, Phone, PostalCode, " +
|
|
"Region from Customers where ContactName like @ContactName";
|
|
|
|
DbParameters dbParameters = CreateDbParameters();
|
|
dbParameters.Add("ContactName", DbType.String).Value = "M%';
|
|
DataSet customerLikeMDataSet = AdoTemplate.DataSetCreateWithParams(CommandType.Text, selectLike, dbParameters);
|
|
|
|
// customerLikeMDataSet has a table named 'Table' with 12 rows
|
|
}
|
|
|
|
public void DemoDataSetFill()
|
|
{
|
|
DataSet dataSet = new DataSet();
|
|
dataSet.Locale = CultureInfo.InvariantCulture;
|
|
AdoTemplate.DataSetFill(dataSet, CommandType.Text, selectAll);
|
|
}</programlisting>Updating a DataSet can be done using a CommandBuilder,
|
|
automatically created from the specified select command and select
|
|
parameters, or by explicitly specifying the insert, update, delete
|
|
commands and parameters. Below is an example, refer to the SDK
|
|
documentation for additional overloads</para>
|
|
|
|
<programlisting language="csharp">public class DataSetDemo : AdoDaoSupport
|
|
{
|
|
private string selectAll = @"select Address, City, CompanyName, ContactName, " +
|
|
"ContactTitle, Country, Fax, CustomerID, Phone, PostalCode, " +
|
|
"Region from Customers";
|
|
|
|
public void DemoDataSetUpdateWithCommandBuilder()
|
|
{
|
|
DataSet dataSet = new DataSet();
|
|
dataSet.Locale = CultureInfo.InvariantCulture;
|
|
AdoTemplate.DataSetFill(dataSet, CommandType.Text, selectAll, new string[]{ "Customers" } );
|
|
|
|
AddAndEditRow(dataSet);.
|
|
|
|
AdoTemplate.DataSetUpdateWithCommandBuilder(dataSet, CommandType.Text, selectAll, null, "Customers");
|
|
}
|
|
|
|
public void DemoDataSetUpdateWithoutCommandBuilder()
|
|
{
|
|
DataSet dataSet = new DataSet();
|
|
dataSet.Locale = CultureInfo.InvariantCulture;
|
|
AdoTemplate.DataSetFill(dataSet, CommandType.Text, selectAll, new string[]{ "Customers" } );
|
|
|
|
AddAndEditRow(dataSet);.
|
|
|
|
string insertSql = @"INSERT Customers (CustomerID, CompanyName) VALUES (@CustomerId, @CompanyName)";
|
|
IDbParameters insertParams = CreateDbParameters();
|
|
insertParams.Add("CustomerId", DbType.String, 0, "CustomerId"); //.Value = "NewID";
|
|
insertParams.Add("CompanyName", DbType.String, 0, "CompanyName"); //.Value = "New Company Name";
|
|
|
|
string updateSql = @"update Customers SET Phone=@Phone where CustomerId = @CustomerId";
|
|
IDbParameters updateParams = CreateDbParameters();
|
|
updateParams.Add("Phone", DbType.String, 0, "Phone");//.Value = "030-0074322"; // simple change, last digit changed from 1 to 2.
|
|
updateParams.Add("CustomerId", DbType.String, 0, "CustomerId");//.Value = "ALFKI";
|
|
|
|
AdoTemplate.DataSetUpdate(dataSet, "Customers",
|
|
CommandType.Text, insertSql, insertParams,
|
|
CommandType.Text, updateSql, updateParams,
|
|
CommandType.Text, null , null);
|
|
}
|
|
|
|
private static void AddAndEditRow(DataSet dataSet)
|
|
{
|
|
DataRow dataRow = dataSet.Tables["Customers"].NewRow();
|
|
dataRow["CustomerId"] = "NewID";
|
|
dataRow["CompanyName"] = "New Company Name";
|
|
dataRow["ContactName"] = "New Name";
|
|
dataRow["ContactTitle"] = "New Contact Title";
|
|
dataRow["Address"] = "New Address";
|
|
dataRow["City"] = "New City";
|
|
dataRow["Region"] = "NR";
|
|
dataRow["PostalCode"] = "New Code";
|
|
dataRow["Country"] = "New Country";
|
|
dataRow["Phone"] = "New Phone";
|
|
dataRow["Fax"] = "New Fax";
|
|
dataSet.Tables["Customers"].Rows.Add(dataRow);
|
|
|
|
DataRow alfkiDataRow = dataSet.Tables["Customers"].Rows[0];
|
|
alfkiDataRow["Phone"] = "030-0074322"; // simple change, last digit changed from 1 to 2.
|
|
}
|
|
}</programlisting>
|
|
|
|
<para>In the case of needing to set parameter SourceColumn or
|
|
SourceVersion properties it may be more convenient to use
|
|
IDbParameterBuilder.</para>
|
|
</sect2>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="ado-tableadapter-tx">
|
|
<title>TableAdapters and participation in transactional context</title>
|
|
|
|
<para>Typed DataSets need to have commands in their internal DataAdapters
|
|
and command collections explicitly set with a connection/transaction in
|
|
order for them to correctly participate with a surrounding transactional
|
|
context. The reason for this is by default the code generated is
|
|
explicitly managing the connections and transactions. This issue is very
|
|
well described in the article <ulink
|
|
url="http://www.code-magazine.com/Article.aspx?quickid=0605031">System.Transactions
|
|
and ADO.NET 2.0</ulink> by ADO.NET guru Sahil Malik. Spring offers a
|
|
convenience method that will use reflection to internally set the
|
|
transaction on the table adapter's internal command collection to the
|
|
ambient transaction. This method on the class
|
|
<literal>Spring.Data.Support.TypedDataSetUtils</literal> and is named
|
|
<methodname>ApplyConnectionAndTx</methodname>. Here is sample usage of a
|
|
DAO method that uses a VS.NET 2005 generated typed dataset for a
|
|
PrintGroupMapping table.</para>
|
|
|
|
<programlisting language="csharp">public PrintGroupMappingDataSet FindAll()
|
|
{
|
|
|
|
PrintGroupMappingTableAdapter adapter = new PrintGroupMappingTableAdapter();
|
|
PrintGroupMappingDataSet printGroupMappingDataSet = new PrintGroupMappingDataSet();
|
|
|
|
|
|
printGroupMappingDataSet = AdoTemplate.Execute(delegate(IDbCommand command)
|
|
{
|
|
TypedDataSetUtils.ApplyConnectionAndTx(adapter, command);
|
|
adapter.Fill(printGroupMappingDataSet.PrintGroupMapping);
|
|
|
|
return printGroupMappingDataSet;
|
|
})
|
|
as PrintGroupMappingDataSet;
|
|
|
|
return printGroupMappingDataSet;
|
|
}</programlisting>
|
|
|
|
<para>This DAO method may be combined with other DAO operations inside a
|
|
transactional context and they will all share the same
|
|
connection/transaction objects.</para>
|
|
|
|
<para>There are two overloads of the method ApplyConnectionAndTx which
|
|
differ in the second method argument, one takes an IDbCommand and the
|
|
other IDbProvider. These are listed below</para>
|
|
|
|
<programlisting language="csharp">public static void ApplyConnectionAndTx(object typedDataSetAdapter, IDbCommand sourceCommand)
|
|
|
|
public static void ApplyConnectionAndTx(object typedDataSetAdapter, IDbProvider dbProvider)</programlisting>
|
|
|
|
<para>The method that takes IDbCommand is a convenience if you will be
|
|
using AdoTemplate callback's as the passed in command object will already
|
|
have its connection and transaction properties set based on the current
|
|
transactional context. The method that takes an IDbProvider is convenient
|
|
to use when you have data access logic that is not contained within a
|
|
single callback method but is instead spead among multiple classes. In
|
|
this case passing the transactionally aware IDbCommand object can be
|
|
intrusive on the method signatures. Instead you can pass in an instance of
|
|
IDbProvider that can be obtained via standard dependency injection
|
|
techniques or via a service locator style lookup.</para>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="ado-objects">
|
|
<title>Database operations as Objects</title>
|
|
|
|
<para>The <literal>Spring.Data.Objects</literal> and <literal>Spring.Data.Objects.Generic
|
|
</literal>namespaces contains classes that allow one to access the
|
|
database in a more object-oriented manner. By way of an example, one can
|
|
execute queries and get the results back as a list containing business
|
|
objects with the relational column data mapped to the properties of the
|
|
business object. One can also execute stored procedures and run update,
|
|
delete and insert statements.</para>
|
|
|
|
<para><note>
|
|
<para>There is a view borne from experience acquired in the field
|
|
amongst some of the Spring developers that the various RDBMS operation
|
|
classes described below (with the exception of the <link
|
|
linkend="ado-storedproc">StoredProcedure</link> class) can often be
|
|
replaced with straight <literal>AdoTemplate</literal> calls... often
|
|
it is simpler to use and plain easier to read a DAO method that simply
|
|
calls a method on a <literal>AdoTemplate</literal> direct (as opposed
|
|
to encapsulating a query as a full-blown class).</para>
|
|
|
|
<para>It must be stressed however that this is just a
|
|
<emphasis>view</emphasis>... if you feel that you are getting
|
|
measurable value from using the RDBMS operation classes, feel free to
|
|
continue using these classes.</para>
|
|
</note></para>
|
|
|
|
<sect2 xml:id="ado-adoquery">
|
|
<title>AdoQuery</title>
|
|
|
|
<para><literal>AdoQuery</literal> is a reusable, threadsafe class that
|
|
encapsulates an SQL query. Subclasses must implement the
|
|
<methodname>NewRowMapper(..)</methodname> method to provide a
|
|
<literal>IRowMapper</literal> instance that can create one object per
|
|
row obtained from iterating over the <literal>IDataReader</literal> that
|
|
is created during the execution of the query. The
|
|
<literal>AdoQuery</literal> class is rarely used directly since the
|
|
<literal>MappingAdoQuery</literal> subclass provides a much more
|
|
convenient implementation for mapping rows to .NET classes. Another
|
|
implementation that extends <literal>AdoQuery</literal> is
|
|
<literal>MappingadoQueryWithParameters</literal> (See SDK docs for
|
|
details).</para>
|
|
|
|
<!--
|
|
|
|
TODO: add example of subclassing AdoQuery here
|
|
|
|
<para>An example of an AdoQuery subclass to encapsulate an insert
|
|
statement for a 'TestObject' (consisting only name and age columns) is
|
|
shown below</para>
|
|
|
|
<programlisting language="csharp">public class CreateTestObjectNonQuery : AdoQuery
|
|
{
|
|
//...
|
|
}</programlisting>
|
|
|
|
-->
|
|
|
|
</sect2>
|
|
|
|
<sect2 xml:id="ado-mappingadoquery">
|
|
<title>MappingAdoQuery</title>
|
|
|
|
<para><literal>MappingAdoQuery</literal> is a reusable query in which
|
|
concrete subclasses must implement the abstract
|
|
<methodname>MapRow(..)</methodname> method to convert each row of the
|
|
supplied <literal>IDataReader</literal> into an object. Find below a
|
|
brief example of a custom query that maps the data from a relation to an
|
|
instance of the <literal>Customer</literal> class.</para>
|
|
|
|
<programlisting language="csharp">public class TestObjectQuery : MappingAdoQuery
|
|
{
|
|
private static string sql = "select TestObjectNo, Age, Name from TestObjects";
|
|
|
|
public TestObjectQuery(IDbProvider dbProvider)
|
|
: base(dbProvider, sql)
|
|
{
|
|
CommandType = CommandType.Text;
|
|
}
|
|
|
|
protected override object MapRow(IDataReader reader, int num)
|
|
{
|
|
TestObject to = new TestObject();
|
|
to.ObjectNumber = reader.GetInt32(0);
|
|
to.Age = reader.GetInt32(1);
|
|
to.Name = reader.GetString(2);
|
|
return to;
|
|
}
|
|
}</programlisting>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="ado-adononquery">
|
|
<title>AdoNonQuery</title>
|
|
|
|
<para>The <literal>AdoNonQuery</literal> class encapsulates an
|
|
IDbCommand 's ExecuteNonQuery method functionality. Like the
|
|
<literal>AdoQuery</literal> object, an <literal>AdoNonQuery</literal>
|
|
object is reusable, and like all <literal>AdoOperation</literal>
|
|
classes, an <literal>AdoNonQuery</literal> can have parameters and is
|
|
defined in SQL. This class provides two execute methods</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><literal>IDictionary ExecuteNonQuery(params object[]
|
|
inParameterValues)</literal></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>IDictionary ExecuteNonQueryByNamedParam(IDictionary
|
|
inParams)</literal></para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>This class is concrete. Although it can be subclassed (for example
|
|
to add a custom update method) it can easily be parameterized by setting
|
|
SQL and declaring parameters.</para>
|
|
|
|
<programlisting language="csharp">public class CreateTestObjectNonQuery : AdoNonQuery
|
|
{
|
|
private static string sql = "insert into TestObjects(Age,Name) values (@Age,@Name)";
|
|
|
|
public CreateTestObjectNonQuery(IDbProvider dbProvider) : base(dbProvider, sql)
|
|
{
|
|
DeclaredParameters.Add("Age", DbType.Int32);
|
|
DeclaredParameters.Add("Name", SqlDbType.NVarChar, 16);
|
|
Compile();
|
|
}
|
|
|
|
public void Create(string name, int age)
|
|
{
|
|
ExecuteNonQuery(name, age);
|
|
}
|
|
|
|
}</programlisting>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="ado-storedproc">
|
|
<title>Stored Procedure</title>
|
|
|
|
<para>The StoredProcedure class is designed to make it as simple as
|
|
possible to call a stored procedure. It takes advantage of metadata
|
|
present in the database to look up names of in and out parameters.. This
|
|
means that you don't have to explicitly declare parameters. You can of
|
|
course still declare them if you prefer. There are two versions of the
|
|
StoredProcedure class, one that uses generics and one that doesn't.
|
|
Using the StoredProcedure class consists of two steps, first defining
|
|
the in/out parameter and any object mappers and second executing the
|
|
stored procedure.</para>
|
|
|
|
<para>The non-generic version of StoredProcedure is in the namespace
|
|
Spring.Data.Objects. It contains the following methods to execute a
|
|
stored procedure</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><literal>IDictionary ExecuteScalar(params object[]
|
|
inParameterValues)</literal></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>IDictionary ExecuteScalarByNamedParam(IDictionary
|
|
inParams)</literal></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>IDictionary ExecuteNonQuery(params object[]
|
|
inParameterValues)</literal></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>IDictionary ExecuteNonQueryByNamedParam(IDictionary
|
|
inParams)</literal></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>IDictionary Query(params object[]
|
|
inParameterValues)</literal></para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>IDictionary QueryByNamedParam(IDictionary
|
|
inParams)</literal></para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>Each of these methods returns an <literal>IDictionary</literal>
|
|
that contains the output parameters and/or any results from Spring's
|
|
object mapping framework. The arguments to these methods can be a
|
|
variable length argument list, in which case the order must match the
|
|
parameter order of the stored procedure. If the argument is an
|
|
IDictionary it contains parameter key/value pairs. Return values from
|
|
stored procedures are contained under the key
|
|
"<literal>RETURN_VALUE</literal>".</para>
|
|
|
|
<para>The standard in/out parameters for the stored procedure can be set
|
|
programmatically by adding to the parameter collection exposed by the
|
|
property DeclaredParameters. For each result sets that is returned by
|
|
the stored procedures you can registering either an
|
|
<literal>IResultSetExtractor</literal>, <literal>IRowCallback</literal>,
|
|
or <literal>IRowMapper</literal> by name, which is used later to extract
|
|
the mapped results from the returned
|
|
<literal>IDictionary</literal>.</para>
|
|
|
|
<para>Lets take a look at an example. The following stored procedure
|
|
class will call the CustOrdersDetail stored procedure in the Northwind
|
|
database, passing in the OrderID as a stored procedure argument and
|
|
returning a collection of OrderDetails business objects.</para>
|
|
|
|
<programlisting language="csharp"> public class CustOrdersDetailStoredProc : StoredProcedure
|
|
{
|
|
private static string procedureName = "CustOrdersDetail";
|
|
|
|
public CustOrdersDetailStoredProc(IDbProvider dbProvider) : base(dbProvider, procedureName)
|
|
{
|
|
DeriveParameters();
|
|
AddRowMapper("orderDetailRowMapper", new OrderDetailRowMapper() );
|
|
Compile();
|
|
}
|
|
|
|
public virtual IList GetOrderDetails(int orderid)
|
|
{
|
|
IDictionary outParams = Query(orderid);
|
|
return outParams["orderDetailRowMapper"] as IList;
|
|
}
|
|
|
|
}</programlisting>
|
|
|
|
<para>The '<literal>DeriveParameters</literal>' method saves you the
|
|
trouble of having to declare each parameter explicitly. When using
|
|
<literal>DeriveParameters</literal> is it often common to use the Query
|
|
method that takes a variable length list of arguments. This assumes
|
|
additional knowledge on the order of the stored procedure arguments. If
|
|
you do not want to follow this loose shorthand convention, you can call
|
|
the method <literal>QueryByNamesParameters</literal> instead passing in
|
|
a IDictionary of parameter key/value pairs. <note>
|
|
<para>If you would like to have the return value of the stored
|
|
procedure included in the returned dictionary, pass in
|
|
<literal>true</literal> as a method parameter to
|
|
<literal>DeriveParameters</literal>().</para>
|
|
</note></para>
|
|
|
|
<para>The <literal>StoredProcedure</literal> class is threadsafe once
|
|
'compiled', an act which is usually done in the constructor. This sets
|
|
up the cache of database parameters that can be used on each call to
|
|
Query or QueryByNamedParam. The implementation of
|
|
<literal>IRowMapper</literal> that is used to extract the business
|
|
objects is 'registered' with the class and then later retrieved by name
|
|
as a fictional output parameter. You may also register
|
|
<literal>IRowCallback</literal> and
|
|
<literal>IResultSetExtractor</literal> callback interfaces via the
|
|
<literal>AddRowCallback</literal> and
|
|
<literal>AddResultSetExtractor</literal> methods.</para>
|
|
|
|
<para>The generic version of StoredProcedure is in the namespace
|
|
Spring.Data.Objects.Generic. It allows you to define up to two generic
|
|
type parameters that will be used to process result sets returned from
|
|
the stored procedure. An example is shown below</para>
|
|
|
|
<programlisting language="csharp"> public class CustOrdersDetailStoredProc : StoredProcedure
|
|
{
|
|
private static string procedureName = "CustOrdersDetail";
|
|
|
|
public CustOrdersDetailStoredProc(IDbProvider dbProvider) : base(dbProvider, procedureName)
|
|
{
|
|
DeriveParameters();
|
|
AddRowMapper("orderDetailRowMapper", new OrderDetailRowMapper<OrderDetails>() );
|
|
Compile();
|
|
}
|
|
|
|
public virtual List<OrderDetails> GetOrderDetails(int orderid)
|
|
{
|
|
|
|
IDictionary outParams = Query<OrderDetails>(orderid);
|
|
return outParams["orderDetailRowMapper"] as List<OrderDetails>;
|
|
}
|
|
|
|
}</programlisting>
|
|
|
|
<para>You can find ready to run code demonstrating the StoredProcedure
|
|
class in the example 'Data Access' that is part of the Spring.NET
|
|
distribution.</para>
|
|
</sect2>
|
|
</sect1>
|
|
</chapter>
|