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 Type | Description | Template File | ||
| Entity Object | Represents 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 | ||
| Mock Entity Object | Provides default test values for a given Entity Object | \templates\template.adapdev.mockentity.tcs | ||
| Entity Collection | Provides a strongly-typed collection for the respective Entity Object | \templates\adapdev\entitycollection.tcs | ||
| Entity Dictionary |
|
| ||
| Data Access Object |
|
| ||
| Web Service |
|
| ||
| NUnit Test |
|
| ||
| Zanebug Test | Provides 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 file | Provides 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 file | Provides 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 Procedures | Provides stored procedures for all DAO functionality | \templates\adapdev\storedprocedures.tsql |
The most important of these objects is listed below.
For further examples, we recommend looking at the generated NUnit or Zanebug tests.
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.
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.
Northwind Employees Example
The class diagram below shows a specific implementation for the Northwind Employees table
Component Diagram
Depending on the options you select, Codus will generate multiple assemblies. Below is the component diagram for a typical project
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.
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.
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:
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%'"));
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);
}
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);
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
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 keyUnfortunately, 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.
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_XXX | Provides access to the name of the XXX field |
| DATABASE_NAME | Provides access to the name of the database that this table is contained in |
| TABLE_NAME | Provides access to the name of the table |
| TABLE_PRIMARYKEY | If 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);
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%'
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");
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.
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.
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
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
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:
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.
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