Click here to Skip to main content
15,867,686 members
Articles / Web Development / ASP.NET
Article

NHibernate vs. NEO: Face to Face. Part I

Rate me:
Please Sign up or sign in to vote.
4.00/5 (16 votes)
20 Jun 20056 min read 121.9K   50   23
The first article in the Nhibernate vs. NEO series. Introduction to NEO O/R mapping tool.

Introduction

There are many open source O/R mapping frameworks that have been appearing on .NET platform during the last year. Most of them are ugly and only a few of them are ready for real-world development. Among the open source solutions, NEO and NHibernate are at the top in the 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 is a real experience from a 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 the usual O/R mapping problems. Please visit the site: codehaus.

NHibernate

NHibernate is a .NET port of a very popular Java based Hibernate framework. There is a very clear trend these 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. Please visit: NHibernate.

NEO Framework

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

  1. Create domain model (UML diagram, text description in 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 are closer to database while others are 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 the database. I prefer the second way.

Let's take a very simple domain mode. This is a kind of very basic bug tracking system. You may add a project, add bugs, assign them to users and add comments to the bugs. Here is the class diagram:

Image 1

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 know, each class in the domain model has a table in a database (we'll not consider inheritance for now). So, all the objects of Project class will be stored in a table projects.

XML
<?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 confused with the javaName property used in C# based framework. But a rational description exists as you may have guessed. The fact is that NEO uses DTD of Torque O/R mapping tool that was written in Java and for Java.

We have mapped the class and now we should do the same for the class properties. Our Project class has four properties:

  • Name,
  • Description,
  • StartDate,
  • ProjectId

In the database, ProjectId will be an auto incremented Primary Key column. Mapping for all the other properties is very simple.

XML
<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 the Bug class. According to the Domain model, each project has a collection of Bugs. This is a one-to-many relation and in a relational database realm, represented by a simple foreign key.

XML
<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 the Bug objects are stored in bugs table, bug_id is the primary key and project_id is the foreign key in projects table.

XML
<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 to Project object and in the code you will be able to write something like this:

C#
String projectName = bug.Project.Name;
XML
<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 collection of bugs in a Project object, we should add information about bugs into the Project class definition:

XML
<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 this:

XML
<?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 the four classes mapped and ready to generate SQL and Ñ# code for them. NEO has a code generation tool neo.exe that can be found in the [neo root folder]\src\Tools\CmdLineTool folder. It is convenient to create BAT file with all the 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

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

Image 2

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

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

Connecting to database

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

XML
<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:

C#
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 a new project and show the project's list. Oh, we did not finish the connection operation. OK, that's easy. Just instantiate IDataStore and create an instance of ObjectContext. Actually, ObjecyContext is very interesting and it is a unit of work pattern. It stores all the changes of loaded objects and is mandatory for all operations with NEO.

C#
// 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 the user fills in the form and pushes the button, a new project is added into the database. The following event handler will do all the actions:

C#
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 the ProjectFactory class is instantiated. This class is responsible for all operations with the projects: create, retrieve, delete and so on. Then we create a new project. Note that this new project is not there in the database right now. In the next step we set all the project properties from the form's fields. And, finally, we save all the changes in ObjectContext into the database. In our case, NEO will generate the INSERT query.

Now it is required to get all the projects from the database and show them in a list. The code in Page_Load handler is very simple:

C#
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 the projects from the database and use the list as a DataSource for lstProjects DataGrid. That's it.

Conclusion

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

That was just a 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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Belarus Belarus
Lead of TargetProcess Project (www.targetprocess.com).
TargetProcess is an integrated Project Management and Bug Tracking software

Comments and Discussions

 
Generalthree questions Pin
DaProgramma13-Mar-06 22:36
DaProgramma13-Mar-06 22:36 
QuestionNeo + Firebird? Pin
Karobark12-Mar-06 4:04
Karobark12-Mar-06 4:04 
AnswerRe: Neo + Firebird? Pin
KiwiPiet11-Apr-06 11:20
KiwiPiet11-Apr-06 11:20 
GeneralRe: Neo + Firebird? Pin
Karobark12-Apr-06 2:15
Karobark12-Apr-06 2:15 
Thanks,
it works fine.
GeneralOJB.Net and Gentle.Net Pin
princemk13-Feb-06 22:23
princemk13-Feb-06 22:23 
Questionnext article? Pin
jettgamboa19-Oct-05 19:34
jettgamboa19-Oct-05 19:34 
GeneralUsing relations Pin
LarsBuur6-Sep-05 7:43
LarsBuur6-Sep-05 7:43 
QuestionHow to use? Pin
Artem Smirnov3-Aug-05 0:30
professionalArtem Smirnov3-Aug-05 0:30 
AnswerRe: How to use? Pin
firefalcon3-Aug-05 4:11
firefalcon3-Aug-05 4:11 
GeneralRe: How to use? Pin
firefalcon3-Aug-05 4:16
firefalcon3-Aug-05 4:16 
GeneralRe: How to use? Pin
Artem Smirnov4-Aug-05 9:09
professionalArtem Smirnov4-Aug-05 9:09 
QuestionScalability? Pin
Martin Jericho30-Jun-05 15:25
Martin Jericho30-Jun-05 15:25 
AnswerRe: Scalability? Pin
Donsw22-Jul-05 10:52
Donsw22-Jul-05 10:52 
GeneralRe: Scalability? Pin
Martin Jericho24-Jul-05 13:48
Martin Jericho24-Jul-05 13:48 
GeneralUML Tool Pin
John Morales30-Jun-05 8:48
John Morales30-Jun-05 8:48 
GeneralRe: UML Tool Pin
firefalcon30-Jun-05 9:21
firefalcon30-Jun-05 9:21 
GeneralRe: UML Tool Pin
morales2k130-Jun-05 9:39
morales2k130-Jun-05 9:39 
GeneralExcellent, but don't forget LLBLGenPro Pin
Jeff Deville29-Jun-05 6:43
Jeff Deville29-Jun-05 6:43 
GeneralRe: Excellent, but don't forget LLBLGenPro Pin
radasys30-Jun-05 20:21
radasys30-Jun-05 20:21 
GeneralGood read!!! Pin
Rajesh Pillai20-Jun-05 18:30
Rajesh Pillai20-Jun-05 18:30 
QuestionAnd for NHibernate ... ? Pin
Sebastien Lorion20-Jun-05 13:06
Sebastien Lorion20-Jun-05 13:06 
AnswerRe: And for NHibernate ... ? Pin
firefalcon20-Jun-05 20:56
firefalcon20-Jun-05 20:56 
GeneralNice Pin
bigals20-Jun-05 12:33
bigals20-Jun-05 12:33 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.