Ayende @ Rahien

Oren Eini aka Ayende Rahien CEO of Hibernating Rhinos LTD, which develops RavenDB, a NoSQL Open Source Document Database.

Get in touch with me:

oren@ravendb.net

+972 52-548-6969

Posts: 7,357 | Comments: 50,740

Privacy Policy Terms
filter by tags archive
time to read 5 min | 907 words

I managed to build an addon to NHibernate that would allows you to use generic collections in NHibernate. Well, one collection, EntitySet<T>, but it's the principal that matter, and it's very easy to write additional classes to do the same for the other collections if you really wants it.

I used a custom PropertyAccessor, and I'm planning on releasing it tomorrow (once I wrote enough tests). But here is the client code that you'll have to write:

Post post = new Post();
blog.Posts.Add(post);
//Or you could do:
//post.Blog = blog;
//But you don’t have to do both
session.Save(post);

Here is how it looks like to the author of the classes:

public class Blog
{
 ...
 
 EntitySet<Post> _posts;
 
 public ICollection<Post> Posts
 {
  get { return _posts; }
 }
 
 public Blog()
 {
  _posts = new EntitySet<Post>(
   delegate (Post p) { p.Blog = this; },
   delegate (Post p) { p.Blog = null; }
   );
 }
 
 ...
}
public class Post
{
 ...
 
 EntityRef<Blog> _blog;
 
 public Blog Blog
 {
  get { return _blog.Value; }
  set { _blog.Value = value; }
 }
 
 public Post()
 {
  _blog = new EntityRef<Blog>(
   delegate(Blog b) { b.Posts.Add(this); }
   delegate(Blog b) { b.Posts.Remove(this); }
   );
 }
 
 ...
}

This has a lot of goodies in it:

  • The users gets the strongly typed generic collections and properties that they come to expect in .Net 2 (ICollection<Post> Post & Blog Blog, respectively).
  • The author of the class doesn't really have to do something special, just tell the EntitySet & EntityRef how it wants the relationships to act when you add/remove or change/clear the reference.
  • Nothing special that you need to do (beyond giving a special access strategy to the Posts property in the mapping, but I'll add this tomorrow to Active Record, so this would be a piece of cake)

The guide lines for this are going to be:

  • Don't expose a setter for the EntitySet<>. This is genenrally a sound advice anywhere where you're exposing collections to the outside world.
  • Don't expose an EntityRef<> directly, use its Value property inside the property and let it manage the connections for you.
  • The most important guide line is going to be: Have fun, because you just saved a lot of time :-)

 

time to read 2 min | 317 words

My first attempt in using generic collections in NHibernate was to just make NHibernate grok my custom collection. I took a look in the code, and it seemed rather straightforward to do so. I posted to the developer list and got the following response from Sergey, the lead developer:

  1. Have your collection class implement an interface (class MyCollection: IMyCollection)
  2. Create NHibernate-specific wrapper class (PersistentMyCollection : PersistentCollection, IMyCollection) which functions as a proxy for the real collection. All methods from IMyCollection that PersistentMyCollection implements have a Read() or Write() at the beginning, and then delegate to the real collection. PersistentMyCollection should also implement some abstract methods from PersistentCollection, these mostly have to do with snapshots and loading process.
  3. Create MyCollectionType which will derive from PersistentCollectionType and implement methods like Wrap (wrapping MyCollection in PersistentMyCollection), Instantiate, Add and Clear, and the rest.
  4. Modify the binder to accept your collection type, and that should be it.

It's straightforward but a long process, and it involves some deep knowledge about the way NHibernate works. Luckily I managed to get it most of it pre-baked by transforming the existing Set collections to use generics. I started to run into problems when I had to dynamically construct generic types, but this article sums it pretty well. I could get the correct info and get it to work.

I decided to wait with that for now, and try a less invasive approach. I'm currently trying to get it to work using a Property Accessors. I'll post my results when I'm done.

time to read 3 min | 500 words

I've been developing in .Net 2.0 [without ReSharper* :-( ], and I'm using NHiberante and Active Record for the data access layer. There were no problems with regard to their usage, but there are things that I don't like in the interface that they gives you. The problem is that you get an untyped collection. This was sort of OK in 1.0/1.1 but it's a really eye sore for developing in 2.0, where I want everything to be a generic type-safe collection.

Another issue with NHibernate is that the syncing between relationships is not automatic. That is to say, the following code is essentially a no-op:

Blog blog = session.Load(typeof(Blog),1);
blog.Posts.Add(new Post("Briliant Post By Ayende"));
session.Flush();

Since the relationship is maintained on the other side of the connection. (Think about the way it's all laid out in the tables, and you'll understand why.) This is the bare minimum to get it to work:

Post post = new Post("Briliant Post By Ayende")
Blog blog = session.Load(typeof(Blog),1);
post.Blog = blog;
session.Save(post);
session.Flush();

But the above code has a serious shortcoming, it doesn't create the association in the blog, so you would need to re-load the blog from the database to get the change. This is undesirable, naturally, so you can do:

Post post = new Post("Briliant Post By Ayende")
Blog blog = session.Load(typeof(Blog),1);
post.Blog = blog;
blog.Posts.Add(post);
session.Save(post);
session.Flush();

Or create a method AddPost() that will do both operations in one line. This is something that bothers me, I like the natural way of doing blog.Posts.Add(post) to create associations. It's what a developer come to expect. The problem is that you can't add the code to do it to the Add() method on the list since the list is something that NHibernate supplies to you.

On the next installment I'll describe how I'm going to solve this problem.

* I did try, but it's not stable by far yet.

FUTURE POSTS

No future posts left, oh my!

RECENT SERIES

  1. Production postmortem (43):
    05 Aug 2022 - The allocating query
  2. Webinar recording (14):
    26 Jul 2022 - RavenDB & Messaging Transactions
  3. Recording (5):
    25 Jul 2022 - Build your own database at Cloud Lunch & Learn
  4. High performance .NET (7):
    19 Jul 2022 - Building a Redis Clone–Analysis II
  5. Upcoming webinar (2):
    14 Jul 2022 - Involving RavenDB in your Messaging Transactions
View all series

Syndication

Main feed Feed Stats
Comments feed   Comments Feed Stats