Usually I don’t really like generated stuff, somehow it seems like I always reach a certain point where I start cursing the $#$%@! wretched generator. So this post will be all about POCO (Plain Old CLR Objects) using just objects. This time I’ll start from the NHibernate Perspective.
Lets consider the following Model (Continuing from my previous post):
It was quite easy to do even with NHibernate – just write a bunch of classes like:
public class User
{
private IList<Category> _categories = new List<Category>();
public virtual string Username { get; set; }
public virtual string Password { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual IList<Category> Categories
{
get { return _categories ; }
set { _categories = value; }
}
}
Write an hbm (mapping) file:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping
xmlns="urn:nhibernate-mapping-2.2"
assembly="Sample.DomainModel"
namespace="Sample.DomainModel">
<class name="User" table="Users" >
<id name="Username" length="255" type="System.String" >
<generator class="assigned" />
</id>
<property name="Password" />
<property name="FirstName" />
<property name="LastName" />
<bag name="Categories"
table="UserCategory" >
<key column="Moderators_Username" />
<many-to-many class="User"
not-found="ignore"
column="Categories_Id" />
</bag>
</class>
</hibernate-mapping>
And that's about it, you have a simple and persistable object.
The following test will show you how easy it is to do some basic operations on the User entity I’ve created, such as creating a new one, loading an existing one and deleting the entity
public void CreateAndDeleteUserTest()
{
string username = "myusernamefortest";
string firstName = "SomeFirstName";
using (ISession session = OpenSession())
using (ITransaction trn = session.BeginTransaction())
{
var usr = new User
{
FirstName = firstName,
LastName = "SomeLastName",
Username = username,
Password = "SomePaswword"
};
session.Save(usr);
trn.Commit();
}
using (ISession session = OpenSession())
using (ITransaction trn = session.BeginTransaction())
{
var usr = session.Load<User>(username);
Assert.IsNotNull(usr);
Assert.AreEqual(usr.FirstName, firstName);
session.Delete(usr);
trn.Commit();
}
}
Now, can it be done with EF 4.0?
Let’s have a look:
First I Created a Project called “Sample.DomainModel.POCO” this is where I want to create Persistence Ignorant Objects so I’ve added the following classes:
User, Category, Message and Thread.
And got the following model (quite similar to the one at the beginning of this post):
Now I’ve Created a Project to hold the EDM –> “Sample.DomainModel.EF”
I’ve added an EDM file to the project (Right Click, Add new item, ADO .NET Entity Data Model). and named it SampleModel.edmx.
This time I was a bit lazy (or deferred, if you like the MS jargon) and I’ve chosen to generate the model from the database, In my last post I’ve generated the model from the EDM, so I’ve decided to use the same one.
And following the instructions from the following post in the ADO .NET Team Blog, I’ve done the following on the properties of SampleModel.edmx I’ve erased the CustomTool Entry and left it blank.
Now I’ve added the to “Sample.DomainModel.EF” the ForumsEntities class :
public class ForumsEntities : ObjectContext
{
private ObjectSet<Category> _categories = null;
private ObjectSet<User> _users = null;
private ObjectSet<Thread> _threads = null;
private ObjectSet<Message> _messages = null;
public ForumsEntities()
: base("name=ForumsEntities", "ForumsEntities")
{
_categories = CreateObjectSet<Category>();
_users = CreateObjectSet<User>();
_threads = CreateObjectSet<Thread>();
_messages = CreateObjectSet<Message>();
}
public ForumsEntities(string connectionString)
: base(connectionString, "ForumsEntities")
{
}
public ObjectSet<Category> Categories
{
get
{
return _categories;
}
}
public ObjectSet<User> Users
{
get
{
return _users;
}
}
public ObjectSet<Thread> Threads
{
get
{
return _threads;
}
}
public ObjectSet<Message> Messages
{
get
{
return _messages;
}
}
}
And that's about it. I’ve run the same tests I’ve used in my previous post, and for my surprise it actually worked!
Summing up, at least from the basic point of view it is not that hard now to implement POCO with EF 4.0 which makes it a bit more appealing for me, although from what I’ve read at the ADO .NET team blog there are some issues using POCO in regards to Inheritance when using Proxies (Which is also an issue with NHibernate), Change Tracking, and probably in a real life project some other tweaks will occur.
I’ve been using ORM for some time now, and I have quite an acquaintance with NHibernate from its first days, thanks to Oren Eini AKA Ayende. For the last few months with the upcoming buzz on VS 2010 and .Net Framework 4.0 I had a some discussions with colleagues of mine regarding what would be the best choice for an ORM tool should we use a well known and mature solution like NHibernate or should we follow the crowd on the Microsoft path? Personally I think that this kind of questions are a bit like religious questions when you have zealots from each direction. Therefore I’ve decided to start and investigate the issue on my own and try my best to do an unbiased comparison between the two.
In my place of work we have a great team of professionals but we are lacking with a quite basic tool for collaborating Information between each other (a forum), so my test case will be on a simple forum for our consultants group at Sela Technology Center.
From my perspective EF v1 was really premature, so I’m starting my quest with EF 4.0 (Also I like having some fun with new stuff).
In my first attempt to use EF 4.0 I’ve wanted to use the Model First support – this way I can model my domain and than create the underlying database, from my experience this would be a better approach when you are starting a new application from scratch (although in most real life scenarios you would probably have some kind of a legacy database from which you’ll start modeling your domain).
I’ve modeled my domain to the following structure:
Then all you need to do is right click on your EDM designer and choose “Generate Database Script From Model…”
This will produce a script that you can run and generate the appropriate tables and constraint in the database (Currently this feature only works from MS SQL Server 2005 and 2008).
If you’ve ever had to design an application with the database schema as well, this feature can save you quite some time going back and forth between your model and database. Unfortunately my first attempt was unsuccessful, I’ve kept getting errors when trying to create the ObjectContext (maybe this feature will work better in the release) so I’ve started again with a simpler model, that looks like this:
This attempt actually worked so I’ve proceeded with completing my model to desired result.
The nice things about this is that when generating the script (or regenerating after making changes to the model) also the MSL (Mapping Specification Language) is automatically configured with the correct mappings.
So far so good, but does this really work? I’ve written a simple console app and added a few tests AddUser, AddCategories and GetCategoriesFromRoot. For the meantime I’ve decided to avoid getting into testability issues (although this is quite a major issue when thinking about DDD).
private static void AddUser()
{
using (ForumsContainer context = new ForumsContainer())
{
User user = new User
{
Username = "user1",
Password = "password",
FirstName = "SomeFirstName",
LastName = "SommeLastName"
};
context.Users.AddObject(user);
context.SaveChanges();
}
}
private static void AddCategories()
{
using (ForumsContainer context = new ForumsContainer())
{
User moderator = context.Users.FirstOrDefault((item) => item.Username == "berniea");
Category cat1 = new Category
{
Id = Guid.NewGuid(),
Parent = null,
Title = "Category 1 Title",
Description = "A long description",
Moderators = { moderator },
Children = { new Category
{
Id = Guid.NewGuid(),
Title = "Sub Category 2 Title",
Description = "A long description",
Moderators = { moderator }
},
new Category
{
Id = Guid.NewGuid(),
Title = "Sub Category 3 Title",
Description = "A long description",
Moderators = { moderator }
}
}
};
context.Categories.AddObject(cat1);
context.SaveChanges();
}
}
private static void GetCategoriesFromRoot()
{
using (ForumsContainer context = new ForumsContainer())
{
context.ContextOptions.DeferredLoadingEnabled = true;
var cats = from cat in context.Categories
where cat.Parent == null
select cat;
foreach (var item in cats)
{
Console.WriteLine("Category Name: {0}", item.Title);
foreach (var childCat in item.Children)
Console.WriteLine("\t Child Cat Name: {0}", childCat.Title);
}
}
}
Doing something similar with NHibernate is also possible just without the nice designer, unfortunately it will require some coding
.
NHibernate will generate the database schema from the mappings (hbm files) and you’ll still have to code your own classes, which is not necessarily a bad idea (if your really lazy there’s a contrib project that generates classes from hbm files).
I’ve created the same model (just classes and mapping files):
A few mapping files like Category.hbm.xml:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Sample.DomainModel"
namespace="Sample.DomainModel">
<class name="Category" table="Categories" >
<id name="Id" type="System.Guid">
<generator class="guid" />
</id>
<property name="Title" />
<property name="Description" />
<bag name="Moderators" table="UserCategory" >
<key column="Categories_Id"/>
<many-to-many class="User" not-found="ignore" column="Moderators_Username" />
</bag>
<many-to-one name="Parent" class="Category" column="Parent_Id"
not-found="ignore" not-null="false"/>
<bag name="Children" table="Categories" lazy="true"
cascade="save-update" inverse="true">
<key column="Parent_Id"/>
<one-to-many class="Category" not-found="ignore" />
</bag>
</class>
</hibernate-mapping>
And the rest (not REST) is history, or just a few lines of code:
public void CreateSchemaFromConfig()
{
var script = new StringBuilder();
Configuration cfg = GetConfiguration();
new SchemaExport(cfg)
.Execute(s => script.AppendLine(s), false, false);
string wholeScript = script.ToString();
Console.WriteLine(wholeScript);
}
This will produce an SQL script a a bit similar to the one produced by the EDM designer, also you can do a SchemaUpdate after making changes to your model.