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\cs\entity.tcs, \templates\adapdev\cs\entitybase.tcs | ||
| Mock Entity Object | Provides default test values for a given Entity Object | \templates\adapdev\cs\mockentity.tcs | ||
| Entity Collection | Provides a strongly-typed collection for the respective Entity Object | \templates\adapdev\cs\entitycollection.tcs | ||
| Entity Dictionary |
|
| ||
| Data Access Object |
|
| ||
| Web Service |
|
| ||
| NUnit Test |
|
| ||
| 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\cs\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\cs\runbuild.bat | ||
| MS Sql Server Stored Procedures | Provides stored procedures for all DAO functionality | \templates\adapdev\cs\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.
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.SelectById(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.
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.SelectById(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.SelectDatasetById(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
The DAO Framework supports two types of transactions:
Local Transactions
Local transactions allow you to perform transactions on a single database, such as Sql Server. Local transactions are provided via ADO.NET.
To use a local transaction that spans multiple tables or statements, you must create a connection, create a transaction, and then pass the connection and transaction to each action.
// Create the Entity objects EmployeesEntity employee = new EmployeesEntity(); RegionEntity region = new RegionEntity(); // ...set some values // Open the db connection and start the transaction IDbConnection connection = ConnectionFactory.CreateConnection(); connection.Open(); IDbTransaction transaction = connection.BeginTransaction(); // Save the entities to their respective tables using a single transaction EmployeesDAO employeesDAO = new EmployeesDAO(); RegionDAO regionDAO = new RegionDAO(); // Pass the connection and transaction object through for each operation employeesDAO.Save(employee, connection, transaction); regionDAO.Save(region, connection, transaction); connection.Close();
Distributed Transactions
Distributed transactions allow code to span mutliple platforms in a single transaction. For example, a distributed transaction could do send a message via MSMQ, insert a record into SQL Server and then delete a record from Oracle. If any of these operations fail, the entire transaction will rollback. Distributed transactions are possible via COM+ and System.EnterpriseServices. The DAO Framework provides a special class, TransactionScope, which provides you the benefits of distributed transactions but avoids the standard overhead of COM+ (i.e. dynamic registration, strong-named assemblies, etc.)
In order to use distributed transactions, you need to have Windows XP Service Pack 2, Windows 2003, or Windows XP Service Pack 1 with Hotfix 828741.
// Create the Entity objects EmployeesEntity employee = new EmployeesEntity(); RegionEntity region = new RegionEntity(); // ...set some values EmployeesDAO employeesDAO = new EmployeesDAO(); RegionDAO regionDAO = new RegionDAO(); // Save them using a distributed transaction using(Adapdev.Transactions.TransactionScope transaction = new TransactionScope()){ employeesDAO.Save(employee); regionDAO.Save(region); transaction.Complete(); }
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
nunit222\nunit.framework.dll
[output directory]\dao.dll
If you are using NUnit 2.1.4, you can use the nunit214\nunit.framework.dll instead
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
The nHibernate templates provide an easy way to generate all classes, mappings, tests and configuration information for a specific database. This gives you the power of nHibernate, but removes a lot of the tedious coding tasks that are normally involved. By generating nHibernate code via Codus, you can save a substantial amount of time.
Below is an overview of the major object types, their description and associated template files.
Table 4. 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\nhibernate\entity.tcs | ||
| Entity Mapping File | Provides the nHibernate mappings between an entity and a database table. One hbm is generated per Entity. | \templates\nhibernate\hbm.xml | ||
| Mock Entity Object | Provides default test values for a given Entity Object | \templates\nhibernate\mockentity.tcs | ||
| Entity Collection | Provides a strongly-typed collection for the respective Entity Object | \templates\nhibernate\entitycollection.tcs | ||
| Entity Dictionary |
|
| ||
| Data Access Object |
|
| ||
| Configurator | Loads all configuration information for nHibernate and creates the ISessionFactory | \template\nhibernate\configurator.tcs | ||
| Hibernate Configuration File | Provides nHibernate specific configuration | \template\nhibernate\hibernate.cfg.xml | ||
| NUnit Test |
|
| ||
| 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\nhibernate\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\nhibernate\runbuild.bat |
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.
NHibernate Mapping File
For every XXXEntity object, an XXX.hbm.xml file is generated that contains the table to XXXEntity mappings.
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.SelectById("ANTON");
// 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>
For every table, a DAO object is generated. This object encapsulates the nHibernate details to simplify things, but also exposes the nHibernate ISession if you want to use it directly.
The DAO object contains methods for selecting all records, selecting records by primary key, and selecting records by foreign key.
Retrieving records is straight forward.
Selecting All Records
// Instantiate the DAO for the Northwind Employees table
EmployeesDAO dao = new EmployeesDAO();
// Returns all records stored in the Northwind Employees table
IList employees = dao.SelectAll();
// Iterate through each item and write it to the Console
foreach(EmployeesEntity employee in employees){
Console.WriteLine(employee);
}
Selecting a Specific Record
// Instantiate the DAO for the Northwind Employees table
EmployeesDAO dao = new EmployeesDAO();
// Return Employee #2
EmployeesEntity employee = dao.SelectById(2);
// Write it to the console
Console.WriteLine(employee);
Selecting Specific Records by Foreign Key
// Instantiate the DAO for the Northwind Employees table
EmployeesDAO dao = new EmployeesDAO();
// Returns all employees that report to Employee #2.
IList employees = dao.SelectAllByReportsTo(2);
// Iterate through each item and write it to the Console
foreach(EmployeesEntity employee in employees){
Console.WriteLine(employee);
}
Getting the Total Number of Records
// Instantiate the DAO for the Northwind Employees table
EmployeesDAO dao = new EmployeesDAO();
// Get the total number of records for the Employees table
int count = dao.Count;
Custom Queries
The DAO object exposes the underlying nHibernate API. Below is an example of a custom query using the nHibernate API directly. Please see nHibernate documentation for guidance on custom queries.
// Instantiate the DAO for the Northwind Employees table
EmployeesDAO dao = new EmployeesDAO();
// Get the total number of records for the Employees table using the nHibernate API
int count = 0;
ISession session = dao.ISessionFactory.OpenSession();
IList results = session.Find("select count(o.EmployeeID) from Test.EmployeesEntity as o");
if(results[0] != null) count = (int)results[0];
session.Close();Modifying records is done easily via the Save and Delete methods.
Creating a new record
EmployeesDAO dao = new EmployeesDAO();
EmployeesEntity employee = new EmployeesEntity();
employee.FirstName = "John";
employee.LastName = "Doe";
// ... set the rest of the values
// Save the record
dao.Save(employee);
Editing an existing record
EmployeesDAO dao = new EmployeesDAO();
// Get employee #2
EmployeesEntity employee = dao.SelectById(2);
// Edit some values
employee.FirstName = "John";
employee.LastName = "Doe";
// Update the record
dao.Save(employee);Deleting a record
EmployeesDAO dao = new EmployeesDAO();
// Get employee #2
EmployeesEntity employee = dao.SelectById(2);
// Delete the record
dao.Delete(employee);