|

|
OVERVIEW |
|
|
 |
This is part 2 of the
series, part 1 is available
here. This part
begins a review of the backend implementation of the
Contact
Manager example. To compile and run the server-side
code you need to have the following software
installed:
Part 1 consists of
the following sections:
Part 3 reviews the
code for handling data updates. It is available
here.
If you have any comments about the article, or
have any questions about the product and the
integration of Flex Data Management Services in WebORB, post
them to the our interest group at:
http://groups.yahoo.com/group/flashorb/ |
|
|

|
CLASS DESIGN |
|
|
 |
The
architecture diagram from Part1 depicts the
Data
Destination box as the server-side component
responsible for executing individual data management
operations. The diagram below shows a drilldown view
into the design of the example's data destination:

The
design includes three classes: ContactAssembler,
ContactDAO and Contact. The assembler class is the
main interface between WebORB and the data. It
contains several methods responsible for retrieving
or modifying data in the data store.
ContactAssembler delegates all data management
operations to ContactDAO, which interfaces directly
with the database using .NET's OleDb API. The
Contact class represents a record in the database.
As you will see shortly the class is optional, since
WebORB provides a way to manage data without
specific generalization of a database record.
|
|
|

|
DATABASE DESIGN |
|
|
 |
The database consists
of a single table - contact. Each row in the table
represents a record of contact information with person's
name, address and phone number:
 |
|

|
CODE REVIEW - READING DATA |
|
|
 |
Below are the methods from the
Examples.Flex.contact.ContactDAO class responsible for
fetching data from the database:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
51
52
53
54
55
56
57 |
namespace Examples.Flex.contact
{
public
class
ContactDAO
{
private
static
string
cnxString
=
"very long cnx string goes here";
public
IList
getContacts()
{
return
loadContactsAsArray(
"SELECT * FROM contact ORDER BY lastName"
);
}
public
IList
getContacts(
string
name )
{
return
loadContactsAsArray(
"SELECT * FROM contact WHERE firstName LIKE '%"+
name +
"%'
OR lastName LIKE '%"
+ name +
"%' ORDER BY lastName"
);
}
private
ArrayList
loadContactsAsArray(
string query
)
{
ArrayList
list =
new
ArrayList();
OleDbConnection
connection =
null;
OleDbCommand
command =
null;
OleDbDataReader
reader =
null;
try
{
connection =
new
OleDbConnection(
cnxString );
command =
new
OleDbCommand(
query, connection );
connection.Open();
reader = command.ExecuteReader();
while(
reader.Read() )
{
Contact
contact =
new
Contact();
contact.contactId =
reader.GetInt32( reader.GetOrdinal(
"contactId"
) );
contact.firstName =
GetString( reader,
"firstName"
);
contact.lastName =
GetString( reader,
"lastName"
);
contact.address =
GetString( reader,
"address" );
contact.city = GetString(
reader,
"city" );
contact.zip = GetString(
reader,
"zip" );
contact.state = GetString(
reader,
"state" );
contact.phone = GetString(
reader,
"phone" );
list.Add( contact );
}
}
finally
{
if( reader
!=
null )
reader.Close();
connection.Close();
}
return list;
}
.... |
The getContacts() method without
arguments (lines 7-9) fetches all records from the
database. The other getContacts method (lines 12-17)
loads all the contacts where either first name or last
name contain the given string. Both methods reuse the
same logic by providing different SQL queries (lines 9
and 14). The method with the logic for executing a
SELECT query (lines 19-56) creates a connection, command
and runs the query (lines 28-31). The code processes all
records fetched from the database in a loop and creates
a Contact object for each record (lines 35-43). The
method returns a collection of all contact objects as an
ArrayList.
The ContactAssembler class delegates to
the ContactDAO.getContacts methods to load the data
(lines 8 and 14 in the code below).
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 |
namespace
Examples.Flex.contact
{
public
class
ContactAssembler
{
public
object
loadContacts()
{
ContactDAO
dao =
new
ContactDAO();
return
dao.getContacts();
}
public
object
loadContacts(
string name
)
{
ContactDAO
dao =
new
ContactDAO();
return
dao.getContacts( name );
}
...... |
WebORB invokes the loadContacts methods
from the ContactAssembler class when a Flex client
requests data from the data store or runs a search
query. A mechanism to register ContactAssembler with
WebORB is described later in this article.
|
|

|
CODE REVIEW - READING
DATA -A DATASET APPROACH |
|
|
 |
There is an alternative approach for
data fetching implemented in the ContactDAO class. The
approach avoids creation of Contact objects for each
data row and simplifies the implementation:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 |
namespace Examples.Flex.contact
{
public
class
ContactDAO
{
private
static
string
cnxString
=
"very long cnx string goes here";
public
DataSet
getContacts()
{
return
loadContactsAsDataSet(
"SELECT * FROM contact ORDER BY lastName"
);
}
public
DataSet getContacts(
string
name )
{
return
loadContactsAsArray(
"SELECT * FROM contact WHERE firstName LIKE '%"+
name +
"%'
OR lastName LIKE '%"
+ name +
"%' ORDER BY lastName"
);
}
private
DataSet
loadContactsAsDataSet(
string
query )
{
DataSet
dataset =
new
DataSet();
OleDbDataAdapter
dataAdapter =
new
OleDbDataAdapter(
query, cnxString );
dataAdapter.Fill( dataset );
return
dataset;
}
..... |
The primary differences between this and
the previous approaches are:
-
The return type of the getContacts()
methods (lines 7 and 12) - the return type is
System.Data.DataSet
-
The implementation of the method
that executes a database query (lines 19-25) - the
new approach creates an untyped DataSet
Below is a modified version of
ContactAssembler supporting the DataSet approach:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 |
using Weborb.Data;
namespace
Examples.Flex.contact
{
public
class
ContactAssembler
{
[ReturnType(
"samples.contact.Contact"
) ]
public
object
loadContacts()
{
ContactDAO
dao =
new
ContactDAO();
return
dao.getContacts();
}
[ReturnType(
"samples.contact.Contact"
) ]
public
object
loadContacts(
string name
)
{
ContactDAO
dao =
new
ContactDAO();
return
dao.getContacts( name );
}
...... |
Notice the method attributes on lines 7
and 14. Since the getContacts methods return an untyped
DataSet, WebORB needs additional information on the data
type contained in the return value from the
client perspective. When WebORB sends data query
result to the client application, it needs to serialize
each record as a samples.contact.Contact client-side
type. In the first approach, that information came from
a configuration file (Examples.Flex.contact.Contact is
mapped to samples.contact.Contact), but in the DataSet
approach, the Contact class is not used, so method
attribute comes to the rescue.
|
|

|
CONFIGURATION |
|
|
 |
ContactAssembler is the example's only
class WebORB interfaces with to handle all data
management operations. The class must be registered in
flex-data-service.xml located in the WEB-INF\flex
folder:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 |
<destination
id="contact">
<adapter
ref="dotnet-dao"
/>
<properties>
<source>Examples.Flex.contact.ContactAssembler</source>
<scope>application</scope>
<metadata>
<identity
property="contactId"/>
</metadata>
<network>
<session-timeout>20</session-timeout>
<paging
enabled="false"
pageSize="10"
/>
</network>
<server>
<fill-method>
<name>loadContacts</name>
</fill-method>
<fill-method>
<name>loadContacts</name>
<params>System.String</params>
</fill-method>
</server>
</properties>
</destination> |
Notice full name of the class on line 5.
The <scope> element on line 6 specifies that only one
instance of the ContactAssembler class should be
created. A registration for both loadContacts methods of
the class appears on lines 18-25. |