3. Adapdev Template Projects

3.1. DAO Framework

3.1.1. Overview

The DAO Framework provides a well-architected way to access databases. For every table in a database, the following items can be generated:

Table 2. Generated Objects

Object TypeDescriptionTemplate File
Entity ObjectRepresents a table row in the database. This object is focused purely on data storage, and is serializable to allow for distributed applications. This allows for data to be easily transferred between the data tier and business tier (also known as a Transfer Object).\templates\adapdev\entity.tcs, \templates\adapdev\entitybase.tcs
Mock Entity ObjectProvides default test values for a given Entity Object\templates\template.adapdev.mockentity.tcs
Entity CollectionProvides a strongly-typed collection for the respective Entity Object\templates\adapdev\entitycollection.tcs
Entity Dictionary
Provides a strongly-typed collection for the respective Entity Object
\templates\adapdev\entitydictionary.tcs
Data Access Object
Provides all database operations, such as saving, deleting, updating and retrieving records. The Data Access Object contains numerous features that enhance or wrap existing ADO.NET functionality.
\templates\adapdev\abstractdaobase.tcs, \templates\adapdev\abstractdao.tcs, \templates\adapdev\dbconstants.tcs
Web Service
Provides DAO functionality via a Web Service
\templates\adapdev\webservice.tcs, \templates\adapdev\webservice.asmx.tcs, \templates\adapdev\webservice.constants.tcs
NUnit Test
Provides a group of tests to verify DAO and Entity Object functionality using the NUnit 2.2 framework.
\templates\adapdev\nunit.tcs, \templates\adapdev\unittest.tcs
Zanebug TestProvides a group of tests to verify DAO and Entity Object functionality using the Zanebug 1.4.X framework.\templates\adapdev\zanebug.tcs, \templates\adapdev\unittest.tcs
NAnt build fileProvides a build file for automatic compilation of all project output. Codus includes a copy of NAnt, in case the target computer doesn't have NAnt installed\templates\adapdev\nant.build
Build utility fileProvides a quick way to launch the NAnt build process. Simply click on the file, and the build will kick off. This alleviates any command line tinkering.\templates\adapdev\runbuild.bat
MS Sql Server Stored ProceduresProvides stored procedures for all DAO functionality\templates\adapdev\storedprocedures.tsql

The most important of these objects is listed below.

Note

For further examples, we recommend looking at the generated NUnit or Zanebug tests.

3.1.2. Architecture

At its most basic, the architecture is composed of one Data Access Object (DAO) and one Entity object per table. The DAO object is a MarshalByRef object, allowing for remote invocation. The Entity object is Serializable, allowing for transfer between remote applications.

Figure 1. DAO Framework Class Diagram (Basic)

DAO Framework Class Diagram (Basic)

Class Diagram

If the full power of the framework is generated, several classes are created addressing different aspects of the architecture. The class diagram below shows the relationship between the various generated classes.

Figure 2. DAO Framework Class Diagram

DAO Framework Class Diagram

Northwind Employees Example

The class diagram below shows a specific implementation for the Northwind Employees table

Figure 3. DAO Framework - Northwind Employees Example

DAO Framework - Northwind Employees Example

Component Diagram

Depending on the options you select, Codus will generate multiple assemblies. Below is the component diagram for a typical project

Figure 4. DAO Framework Component Diagram

DAO Framework Component Diagram

3.1.3. Entity Object

The Entity Object is an object representation of a table row, with some added features.

For every table or view, an Entity will be created in the form of XXXEntity. For example, the Northwind database contains an Employees table. An EmployeesEntity will be created to store information from that table.

Child Properties

If you have foreign keys defined in your database tables, Codus will automatically create child properties in your XXXEntity. For example, the Northwind database contains the Employees table. The Orders table stores the EmployeeId for the Employee that created that order. Since the link from Employee-EmployeeId to Orders-EmployeeId is a foreign key relationship, Codus will automatically create a property called OrdersByEmployeeId

// Retrieve the orders placed by Employee #22
EmployeesDAO dao = new EmployeesDAO();
EmployeeEntity employee = dao.SelectOne(22) as EmployeeEntity ; // SelectOne uses the primary key value as the parameter
foreach(OrdersEntity order in employee.OrdersByEmployeeID)
{
   Console.WriteLine(order.OrderDate); // Display the date of the order
}

Serialization

All Entity Objects are Serializable, which means they can be transferred between remote applications, and/or stored to a file, etc.

The following code shows an example of serializing an Entity Object (using a Northwind database example):

CustomersDAO dao = new CustomersDAO();
CustomersEntity customer = dao.SelectOne("ANTON") as CustomersEntity;

// Serialize using Adapdev.Serialization.Serializer...a utility class.
Console.WriteLine(Adapdev.Serialization.Serializer.SerializeToXml(customer));

This produces the following output:

<?xml version="1.0"?>
<CustomersEntity xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <Address>Mataderos  2312</Address>
  <City>Mexico D.F.</City>
  <CompanyName>Antonio Moreno TaquerC-a</CompanyName>
  <ContactName>Antonio Moreno</ContactName>
  <ContactTitle>Owner</ContactTitle>
  <Country>Mexico</Country>
  <CustomerID>ANTON</CustomerID>
  <Phone>(5) 555-3932</Phone>
  <PostalCode>05023</PostalCode>
</CustomersEntity>

ToString

All Entity Objects also override the ToString() method, and will create a string printout of all the objects property names, their object types and values. This is useful for debugging purposes. Below is an example (using a Northwind database):

// Call the ToString method
EmployeesDAO dao = new EmployeesDAO();
EmployeesEntity employee = dao.SelectOne(9) as EmployeesEntity;
Console.WriteLine(employee);

This produces the following output:

Properties for: EmployeesEntity
	Address(System.String): 7 Houndstooth Rd.
	BirthDate(System.DateTime): 1/27/1966 12:00:00 AM
	City(System.String): London
	Country(System.String): UK
	EmployeeID(System.Int32): 9
	Extension(System.String): 452
	FirstName(System.String): Anne
	HireDate(System.DateTime): 11/15/1994 12:00:00 AM
	HomePhone(System.String): (71) 555-4444
	LastName(System.String): Dodsworth
	Notes(System.String): Anne has a BA degree in English from St. Lawrence College.  She is fluent in French and German.
	Photo(System.Byte[]): System.Byte[]
	PhotoPath(System.String): http://accweb/emmployees/davolio.bmp
	PostalCode(System.String): WG2 7LT
	Region(System.String): 
	ReportsTo(System.Int32): 5
	Title(System.String): Sales Representative
	TitleOfCourtesy(System.String): Ms.

3.1.4. Data Access Object

The bulk of the DAO Framework capability lies in the Data Access Object (DAO). The DAO inherits from the Adapdev.Data.AbstractDAO class, which is part of the free and open-source Adapdev.NET project.

For every table in a database, a corresponding DAO class is generated.

Below are examples of common uses. These examples use code that's generated for the Northwind Employees table:

3.1.4.1. Retrieving Records

Getting the total number of records in a table

EmployeesDAO dao = new EmployeesDAO();
int count = dao.GetCount();

Getting all records in a table

// Using IList
EmployeesDAO dao = new EmployeesDAO();
IList employees = dao.SelectAll();
foreach(EmployeesEntity e in employees){
   Console.WriteLine(e.FirstName);
}

// Populate an ArrayList
EmployeesDAO dao = new EmployeesDAO();
ArrayList employees = dao.SelectAll() as ArrayList;
foreach(EmployeesEntity e in employees){
   Console.WriteLine(e.FirstName);
}

// Populate a strongly-typed collection
EmployeesDAO dao = new EmployeesDAO();
EmployeesEntityCollection employees = new EmployeesEntityCollection(dao.SelectAll());
foreach(EmployeesEntity e in employees){
   Console.WriteLine(e.FirstName);
}

Getting the top 10 records in a table

// Populate an ArrayList
EmployeesDAO dao = new EmployeesDAO();
IList employees = dao.SelectAllWithLimit(10);
foreach(EmployeesEntity e in employees){
   Console.WriteLine(e.FirstName);
}

// Populate a strongly-typed collection
EmployeesDAO dao = new EmployeesDAO();
EmployeesEntityCollection employees = new EmployeesEntityCollection(dao.SelectAllWithLimit(10));
foreach(EmployeesEntity e in employees){
   Console.WriteLine(e.FirstName);
}

Getting all the records in a table, via a DataSet

// Populate a DataSet
EmployeesDAO dao = new EmployeesDAO();
DataSet employeesDS = dao.SelectAllDS();

Getting a specific record

// Retrieve employee record 22
EmployeesDAO dao = new EmployeesDAO();
EmployeeEntity employee = dao.SelectOne(22) as EmployeesEntity; // SelectOne uses the primary key value as the parameter

Getting a DataSet containing a specific record

// Retrieve employee record 22
EmployeesDAO dao = new EmployeesDAO();
DataSet employeeDS = dao.SelectOneDS(22); // SelectOneDS uses the primary key value as the parameter

Getting a custom-defined set of records

// Get records using a custom sql statement
EmployeesDAO dao = new EmployeesDAO();
ArrayList al = new ArrayList(dao.Select("SELECT * FROM Employees WHERE FirstName LIKE 'm%'"));

Note

The above example fills all Entity properties, since a SELECT * statement is being executed. If you use a SELECT statement that only grabs specific columns, then only those matching column properties in the Entity object will be filled. The remaining items will remain unfilled.

Getting a custom-defined set of records, via a DataSet

// Get records using a custom sql statement
EmployeesDAO dao = new EmployeesDAO();
DataSet ds = dao.SelectDS("SELECT * FROM Employees WHERE FirstName LIKE 'm%'");

Getting all records with a specified foreign key

// Get records that contain a specified foreign key
// Codus generates SelectAllByXXX methods for each foreign key in a table
EmployeesDAO dao = new EmployeesDAO();
ArrayList al = new ArrayList(dao.SelectAllByReportsTo(2));
foreach(EmployeesEntity employee in al){
   Console.WriteLine(employee.FirstName);
}
3.1.4.2. Modifying Records

Create a new record

// Create a new record
EmployeesEntity employee = new EmployeesEntity();
employee.FirstName = "Joe";
employee.LastName = "Schmoe";
// ...etc.

EmployeesDAO dao = new EmployeesDAO();
dao.Save(employee);

Note

If you are using an auto-increment column in the database as your primary key, Codus will automatically retrieve the value and update the appropriate object property with the value.

Update an existing record

EmployeesDAO dao = new EmployeesDAO();

// Grab employee 22 and update who they report to
EmployeesEntity employee = dao.SelectOne(22) as EmployeesEntity;
employee.ReportsTo = 5;

dao.Update(employee);

Delete an existing record

EmployeesDAO dao = new EmployeesDAO();
dao.Delete(22); // Delete using the primary key
3.1.4.3. Dealing with Connections

By default, each DAO call creates and destroys a database connection. For example:

// Connection is created and destroyed behind the scenes
EmployeesDAO dao = new EmployeesDAO();
dao.Delete(22); // Delete using the primary key

Unfortunately, creating and destroying connections is an expensive operation. If you have a program that is making multiple DAO calls, you're better served using only one open connection. Almost all DAO methods support the ability to pass in an open database connection. Below is an example:

EmployeesDAO dao = new EmployeesDAO();

// The using directive automatically closes the connection.  
// See MS.NET documentation for details.
// If you don't use the "using" directive, then you'll need to explicitly
// open and close the connection.
using(IDbConnection connection = dao.CreateConnection()){
   // open the connection
   connection.Open();

   // get an existing record
   EmployeesEntity employee = dao.SelectOne(8, connection) as EmployeesEntity;
   employee.LastName = "Hilton";

   // update it
   dao.Update(employee, connection);

   // delete another record
   dao.Delete(22);
}

The above example will achieve better performance, since it is only using 1 open connection for all calls, vs. 3 separate connections.

3.1.4.4. Building Sql Statements

It's not always easy to remember the column names or table names for a database table. To make things easier, each generated DAO class contains constant values for the column names, table name and database name. This makes writing sql statements much easier. The following items are generated:

Table 3. Constants

COLUMN_XXXProvides access to the name of the XXX field
DATABASE_NAMEProvides access to the name of the database that this table is contained in
TABLE_NAMEProvides access to the name of the table
TABLE_PRIMARYKEYIf the table has a primary key, then this provides access to the name of the primary key field

Below is an example for using these constants:

string sql = String.Format("SELECT {0}, {1}, {2} FROM {3} WHERE {4} LIKE 'm%'",
							EmployeesDAO.COLUMN_FIRSTNAME,
							EmployeesDAO.COLUMN_LASTNAME,
							EmployeesDAO.COLUMN_EMPLOYEEID,
							EmployeesDAO.TABLE_NAME,
							EmployeesDAO.COLUMN_FIRSTNAME);

Note

If the column name contains a space - for example First Name - then you'll need to surround the column name with [ ]. For example:

string sql = String.Format("SELECT [{0}] FROM {3} WHERE [{4}] LIKE 'm%'",
							EmployeesDAO.COLUMN_FIRSTNAME,
							EmployeesDAO.TABLE_NAME,
							EmployeesDAO.COLUMN_FIRSTNAME);

In the example above, you'll note that [ ] has been added. The resulting statement would be:

SELECT [First Name] FROM Employees WHERE [First Name] LIKE 'm%'
3.1.4.5. Executing Statements

Beyond the capabilities listed above for selecting records, etc., the DAO Framework has built in support for executing custom statements that don't return anything.

EmployeesDAO dao = new EmployeesDAO();
dao.ExecuteNonQuery("UPDATE FirstName SET FirstName = 'John' WHERE EmployeeID = 5");
3.1.4.6. Adding Custom Code

The DAO Framework generates two classes:

  • XXXDAOBase

    This class contains the core functionality. Modifications can occur here, but it is not recommended that custom code be added, since this file is overwritten by the Codus engine each time generation takes place.

  • XXXDAO

    This class inherits from XXXDAOBase. The Codus engine will not overwrite this file upon subsequent generations, so it is recommended that all custom code be placed here.

    Warning

    The behavior of file overwriting differs between straight generation and generation to VS.NET 2003. In straight generation (i.e. simply generating to an output directory), the XXXDAOBase will be overwritten each time, whereas the XXXDAO file is only created once, and not overwritten.

    In the case of VS.NET 2003, all code is generated to an output directory and then copied over to a VS.NET 2003 solution directory. If your custom code is in the XXXDAO within the output directory, it will be preserved. However, if you've made changes within VS.NET and then regenerate into the same solution, your changes could be lost. Please be careful when dealing with VS.NET generation so that you don't lose your custom code.

3.1.5. Building the Framework

VS.NET 2003

The DAO Framework allows for generation directly into VS.NET 2003. Since the DAO Framework can generate hundreds of files, it is recommended that you use this capability.

NAnt

You can select NAnt generation, and a .build file will be placed in the output directory. You can launch the build by clicking on the runbuild.bat in the output directory, which will launch NAnt and build the generated output.

Manual

If you choose not to NAnt or VS.NET 2003, all of the binaries necessary are located in the [Codus Install Location]\shared directory. Here are the necessary references:

  • All Projects

    • \adapdev\Adapdev.dll

    • \adapdev\Adapdev.Data.dll

  • NUnit Tests

    • nunit220\nunit.framework.dll

    • [output directory]\dao.dll

      Note

      If you are using NUnit 2.1.4, you can use the nunit214\nunit.framework.dll instead

  • Zanebug Tests

    • \adapdev\Adapdev.UnitTest.dll

    • \adapdev\Adapdev.UnitTest.Core.dll

    • [output directory]\dao.dll

  • Web Services

    • [output directory]\dao.dll

3.1.6. Modifying the Framework

The framework uses the Velocity template language for all code generation, provided via modified version of NVelocity. The following options are available for making modifications:

  1. Modify the included templates - unfortunately, the internals of these templates are not currently documented. A developer's guide will be provided shortly, but in the mean-time it should be somewhat self-explanatory. CodusIDE will be shortly released, too, which will be the ideal environment for creating and editing templates.

  2. Download the free Adapdev.NET project - much of the code used in this project is built on top of Adapdev.NET. This is where you'll find the AbstractDAO class that all generated XXXDAO classes are based on