Atlas Project: AJAX in ASP.NET 2.0

Very promising project for creating AJAX based applications on ASP.NET 2.0. It seems it will be released next year, and features set looks good. Atlas Project will include new controls with AJAX support, like auto-complete textboxes, drag and drop, client side paging and sorting in DataView and so on; object-oriented script framework; web services integration.

With this feature ASP.NET may greatly improve UI and this will be very good reason for developers to migrate to ASP.NET. We’ll see.

Wicket – One more Java Web Component Framework

Wicket is one more component framework like Tapestry, JSF or ASP.NET. However, it’s light and very simple. Well, ASP.NET is simpler 🙂

One example (displays textarea with submit button and messages list):

public GuestBook()
{
 add(new CommentForm("commentForm"));
 add(commentListView = new ListView("comments", commentList)
 {
     public void populateItem(final ListItem listItem)
     {
         final Comment comment = (Comment)listItem.getModelObject();
         listItem.add(new Label("date", comment.getDate()));
         listItem.add(new MultiLineLabel("text", comment.getText()));
     }
 });
}

NHibernate vs. NEO: Face to Face. Part I

There are many open source O/R mapping frameworks have been
appearing for .NET platform during the last year. Most of them are ugly
and just few of them ready for real-world development. Among open
source solutions, NEO and NHibernate stay on the top of other in O/R
mapping world. That is why we will compare them in this series of
articles. Face to face.

In this article we will introduce NEO framework. I have very good experience with NEO, since we use it in TargetProcess project – ASP.NET based agile project management system. The project is quite mature and this will be the real experience from real production project.

Short Intro

NEO

NEO is a framework originally created for .NET. It is quite simple and quite featured to solve almost all usual O/R mapping problems.
http://neo.codehaus.org

NHibernate

NHibernate is a .NET port of very popular Java based Hibernate
framework. There is a very clear trend last years to port all good
tools from Java world to .NET. Hibernate is widely used and has been
evolved into one of the most powerful O/R mapping solutions.
http://nhibernate.sourceforge.net/

NEO Framework

Usage of almost any O/R mapping tool is quite uniform with some deviations. There are six steps to start using NEO:

  1. Create domain model (UML diagram, text description or your mind)
  2. Create domain descriptor in XML format for NEO
  3. Generate C# classes and SQL queries for domain model
  4. Include C# classes into project
  5. Create the database and execute SQL queries
  6. Use NEO framework to handle persistence objects

Creating mappings

Any O/R mapping tool works with domain model. Some of them closer to
database, while some of them closer to domain model. NEO is on the
middle ground. You may create a database and generate domain model
descriptor file, or you may create domain model descriptor and then
generate database. I prefer the second way.

Let’s take very simple domain mode. This is a kid of very basic Bug
tracking system. You may add a project, add bugs, assign them on users
and add comments to the bugs. Here is the class diagram.

Now we should translate this fancy UML picture to NEO language –
model descriptor in XML format. We are starting with project class
mapping. As you maybe know, each class in domain model has a table in a
database (we’ll not consider inheritance for now). So, all objects of Project class will be stored in a table projects.

<?xml version="1.0" encoding="ISO-8859-1" standalone="no" ?>
<!DOCTYPE database SYSTEM "Resources/norque.dtd">
<database name="tp" package="Neo.Web.Model"
defaultIdMethod="native" defaultJavaNamingMethod="underscore">

<table name="projects" javaName="Project">
. . .
</table>

</database>

Well, maybe you are slightly confusing with javaName property in C#
based framework. But a rational description exists as you may guess.
The fact is that NEO uses DTD of Torque O/R mapping tool that was
writing on Java and for Java.

We have mapped the class, and now should do the same for class
properties. Our Project class has four properties: Name, Description,
StartDate and ProjectId. In database, ProjectId will be an
autoincremental Primary Key column. Mappings for all the other
properties are very simple.

<table name="projects" javaName="Project">
<column name="project_id" primaryKey="true" autoIncrement="true"
required="true" type="INTEGER" />
<column name="name" required="true" type="VARCHAR" size="150" />
<column name="description" required="false" type="VARCHAR" size="600" />
<column name="start_date" required="true" type="DATE" />
</table>

Let’s skip mapping of User class, since it is almost the same, and
look right into Bug class. According to Domain Model, each project
could have a collection of Bugs. This is one-to-many relation and in a
Relational Databases realm represented by a simple Foreign Key.

<table name="bugs" javaName="Bug">
<column name="bug_id" primaryKey="true" autoIncrement="true" required="true"
type="INTEGER" />
<column name="name" required="true" type="VARCHAR" size="250" />
<column name="description" required="false" type="VARCHAR" size="2500" />
<column name="effort" required="false" type="DECIMAL" />
<column name="status" required="true" type="INTEGER" />
<column name="project_id" required="true" type="INTEGER" />
<foreign-key foreignTable="projects" name="Project" onDelete="cascade">
<reference local="project_id" foreign="project_id" />
</foreign-key>
</table>

All Bug objects will be stored in bugs table, bug_id will be a
primary key and project_id will be a foreign key on projects table.

<foreign-key foreignTable="projects" name="Project" onDelete="cascade">

Name attribute sets the name of relation. In this case each Bug object will have a reference at Project object and in code you will be able to write something like this:

String projectName = bug.Project.Name;
<reference local="project_id" foreign="project_id" />

Reference sets columns for foreign key. In this case project_id column should exist in bugs table and in projects table.

If we want to have collections of bugs in a project object, we should add information about bugs into project class definition:

<table name="projects" javaName="Project">
. . .
    <iforeign-key foreignTable="bugs" name="Bugs" onDelete="cascade">
<ireference local="project_id" foreign="project_id" />
</iforeign-key>
</table>
public ICollection GetBugs()
{
return new BugList(this.Bugs);
}

Finally, the full mapping will look like that:

<?xml version="1.0" encoding="ISO-8859-1" standalone="no" ?>
<!DOCTYPE database SYSTEM "Resources/norque.dtd">
<database name="tp" package="Neo.Web.Model" defaultIdMethod="native"
defaultJavaNamingMethod="underscore">

<table name="projects" javaName="Project">
<column name="project_id" primaryKey="true" autoIncrement="true"
required="true" type="INTEGER" />
<column name="name" required="true" type="VARCHAR" size="150" />
<column name="description" required="false" type="VARCHAR" size="600" />
<column name="start_date" required="true" type="DATE" />
<iforeign-key foreignTable="bugs" name="Bugs" onDelete="cascade">
    <ireference local="project_id" foreign="project_id" />
</iforeign-key>
</table>

<table name="users" javaName="User">
<column name="user_id" primaryKey="true" autoIncrement="true"
required="true" type="INTEGER" />
<column name="name" required="true" type="VARCHAR" size="150" />
<column name="login" required="true" type="VARCHAR" size="50" />
<column name="password" required="true" type="VARCHAR" size="50" />
</table>

<table name="bugs" javaName="Bug">
<column name="bug_id" primaryKey="true" autoIncrement="true"
required="true" type="INTEGER" />
<column name="name" required="true" type="VARCHAR" size="250" />
<column name="description" required="false" type="VARCHAR" size="2500" />
<column name="effort" required="false" type="DECIMAL" />
<column name="status" required="true" type="INTEGER" />
<column name="project_id" required="true" type="INTEGER" />
<column name="user_id" required="true" type="INTEGER" />
<foreign-key foreignTable="projects" name="Project" onDelete="cascade">
    <reference local="project_id" foreign="project_id" />
</foreign-key>
<foreign-key foreignTable="users" name="Owner">
    <reference local="user_id" foreign="user_id" />
</foreign-key>
<iforeign-key foreignTable="comments" name="Comments" onDelete="cascade">
    <ireference local="bug_id" foreign="bug_id" />
</iforeign-key>
</table>

<table name="comments" javaName="Comment">
<column name="comment_id" primaryKey="true" autoIncrement="true"
required="true" type="INTEGER" />
<column name="text" required="false" type="VARCHAR" size="1000" />
<column name="date" required="true" type="DATE" />
<column name="user_id" required="true" type="INTEGER" />
<column name="bug_id" required="true" type="INTEGER" />
<foreign-key foreignTable="users" name="User">
    <reference local="user_id" foreign="user_id" />
</foreign-key>
<foreign-key foreignTable="bugs" name="Bug" onDelete="cascade">
    <reference local="bug_id" foreign="bug_id" />
</foreign-key>
</table>
</database>

Generating Model Classes

Now we have all four classes mapped and ready to generate SQL and С# code for them. NEO has a code generation tool neo.exe that could be found in [neo root folder]\src\Tools\CmdLineTool folder. It is convenient to create bat file with all required commands.

neo.exe -r Resources -f -sql true -sqldrop true -o Sql model.xml

neo.exe -r Resources -user true model.xml

neo.exe -r Resources -support true -o Base model.xml

First command generates SQL queries and puts them into Sql
folder. Using these queries you may create database for sample
application. Second command generates model classes that may be
modified by user (will not be regenerated by NEO in future). Third
command creates base model classes that will be regenerated by NEO when
model changed. Finally, we will have the structure shown on the picture
below:

After bat file execution, we should just include new classes into our project and change namespaces.

Now we should create a database and execute generated model.sql script from Sql folder.

Connecting to Database

Let’s do the last part of preparations – connect to database. First
of all, create DataStoreFactory class that will create database
connections. There will be two parameters in Web.config

<add key="ConnectionString" value="data source=(local);initial catalog=ormapping;uid=sa;pwd=" />
<add key="DatabaseServer" value="Neo.SqlClient.SqlDataStore,Neo" />

DataStoreFactory has just one Create method:

public IDataStore Create()
{

 string className =
  ConfigurationSettings.AppSettings["DatabaseServer"];

 string conn =
  ConfigurationSettings.AppSettings["ConnectionString"];

 return (IDataStore)Activator.CreateInstance(
  Type.GetType(className + ",Neo"),new object[] {conn});

}

It’s time to have fun with NEO framework. The first application’s
page will add new project and shows projects list. Oh, we did not
finish connection operations. Ok, that’s easy. Just instantiate
IDataStore and create an instance of ObjectContext. Actually,
ObjecyContext is very interesting beats and it is a Unit of Work pattern. It stores all changes of loaded objects and mandatory for all operations with NEO.

// create data store
DataStoreFactory store = new DataStoreFactory();
IDataStore dataStore = store.Create();

// create context for changes tracking
context = new ObjectContext(dataStore);

Persistence

There is a web form on the page, as usual, with several text boxes
and a button. When user fills out the form and pushes the button, new
project should be added into the database. The following event handler
will do all actions:

private void btnAddProject_Click(object sender, System.EventArgs e)
{
// create new project
ProjectFactory projectFactory = new ProjectFactory(context);
Project project = projectFactory.CreateObject();

// set project properties
project.Name = txtName.Text;
project.Description = txtDescription.Text;
project.StartDate = Convert.ToDateTime(txtStartDate.Text);

// save new project into the database
context.SaveChanges();
}

First, we instantiate ProjectFactory. This class responsible for all
operations with projects: create, retrieve, delete and so on. Then we
create a new project. Note that this new project is not in the database
right now. On the next step we set all project properties from form’s
fields. And, finally, we save all changes in ObjectContext into the
database. In our case, NEO will generate INSERT query.

Now it is required to get all projects from database and show them in a list. Code in Page_Load handler will be very simple:

private void Page_Load(object sender, System.EventArgs e)
{
// create data store
DataStoreFactory store = new DataStoreFactory();
IDataStore dataStore = store.Create();

// create context for changes tracking
context = new ObjectContext(dataStore);

// retrieve all projects from database
ProjectList projects = new ProjectFactory(context).FindAllObjects();

// fill datagrid
lstProjects.DataSource = projects;
lstProjects.DataBind();
}

Again, we use ProjectFactory to retrieve all projects from database
and use the list as a DataSource for lstProjects DataGrid. That’s it.

Conclusion

Do you remember times when dynamic SQL or stored procedures had been
used for database operations? NEO or any other good O/R mapping
solution makes life easier and code lighter. A lot.

That was just quick start with NEO. In the next article we will
check more complex topics, like complex queries, possible architectural
solutions for ObjectContext handling, detached database and TDD with
NEO, inheritance problem and so on.