|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionThere 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 IntroNEONEO 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. NHibernateNHibernate 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 FrameworkUsage of almost any O/R mapping tool is quite uniform with some slight deviations. These are the six steps to start using NEO:
Creating mappingsAny 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:
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 <?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 We have mapped the class and now we should do the same for the class properties. Our
In the database, <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 <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 <foreign-key foreignTable="projects" name="Project" onDelete="cascade">
String projectName = bug.Project.Name;<reference local="project_id" foreign="project_id" />
If we want to have collection of bugs in a <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 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 classesNow 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:
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 databaseLet's do the last part of preparations - connecting to database. First of all, create <add key="ConnectionString"
value="data source=(local);initial catalog=ormapping;uid=sa;pwd=" />
<add key="DatabaseServer" value="Neo.SqlClient.SqlDataStore,Neo" />
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 // create data store
DataStoreFactory store = new DataStoreFactory();
IDataStore dataStore = store.Create();
// create context for changes tracking
context = new ObjectContext(dataStore);
PersistenceThere 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: 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 Now it is required to get all the projects from the database and show them in a list. The code in 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 ConclusionDo 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
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||