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); // 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");
// 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.SelectById(9); 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
EmployeesDAO dao = new EmployeesDAO();
List<EmployeesEntity> employees = dao.SelectAll();
foreach(EmployeesEntity e in employees){
Console.WriteLine(e.FirstName);
}
Getting the top 10 records in a table
EmployeesDAO dao = new EmployeesDAO();
List<EmployeesEntity> employees = 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.SelectAllDataSet();
Getting a specific record
// Retrieve employee record 22
EmployeesDAO dao = new EmployeesDAO();
EmployeeEntity employee = dao.SelectById(22); // SelectById 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); // SelectDatasetById 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();
List<EmployeesEntity> al = new List<EmployeesEntity>(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();
List<EmployeesEntity> al = new List<EmployeesEntity>(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. For
transactions, simply use the standard .NET
System.Transactions.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(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 2005
The DAO Framework allows for generation directly into VS.NET 2005. 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 2005, 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
nunit229\nunit.framework.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.
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);