Castle Demo AppActive Record Relations

time to read 21 min | 4078 words

For this part, I'm going to use NHibernate.Generics, which is an extention to NHibernate that allows to use generic collections for relationship between classes. You need to download it from here and add a reference to:

  • NHibernate.Generics.dll
  • Iesi.Collections.dll

Okay, so we have a User, now we need a Bug class, so we can store the information about our bugs. I'm going to skip the parts that I've already covered and move to the new stuff. Here is how the Bug class looks like:

There are three interesting properties here, BugStatus, Text and AssignedTo. Let's go over each one in turn.

BugStatus is an enum defined like this:

public enum BugStatus : byte

{

    Opened              = 1,

    Duplicated          = 2,

    CouldNotRepreduce   = 3,

    Fixed               = 4,

    WontFix             = 5 //Urgh!

}

As you can see, it's just a normal enum (with the backing store specified as a byte). You can also see that I've some strong feeling about Won't Fix bugs :-) Here is how I defined the Status property on the Bug class:

    [Property]

    public BugStatus Status

    {

        get { return _status; }

        set { _status = value; }

    }

It's just a normal property, with the [Property] attribute to decorate it and tell Active Record that it should save it to the database. No special handling required for enums.

The second interesting property is Text, since we might want to store large amount of text there. The issue in this case is a bug in ActiveRecord/NHibernate (which will probably be fixed in the next release) where text larger than 4,000 character is being silently truncated. The fix for this is explicitly stating that this property may contain large amount of text. All we need to do is specify the column type as StringClob, like this:

    [Property(ColumnType="StringClob")]

    public string Text

    {

        get { return _text; }

        set { _text = value; }

    }

Now we get to the AssignedTo property, which is a fair bit more interesting. I'm following the Hot Potato Bug Tracking phylosophy, which basically says that a bug always has someone assigned to take care of it. This mean that there is a connection between a Bug and a User. How do we specify this?

    [BelongsTo]

    public User AssignedTo

    {

        get { return _assignedTo.Value; }

        set { _assignedTo.Value = value; }

    }

This requires some explanation, in general, just specifying that this Bug [BelongsTo] a user is enough. But right now NHibernate doesn't support generic collections right now, in order to support that you need to use NHibernate.Generics, which provides this functionality. The _assignedTo variable isn't of type User, it's actually defined as:

    EntityRef<User> _assignedTo;

 

    public Bug()

    {

        _assignedTo = new EntityRef<User>(

            delegate(User user) { user.AssignedBugs.Add(this); },

            delegate(User user) { user.AssignedBugs.Remove(this); }

            );

    }

What this basically says is that _assignedTo is a reference to another class, and the part in the constructor we define the actions that will happen when the reference is changed. In this case, we add or remove the current instance to the Bugs collection of the user. This ensure that the right thing happens both in the database and in our objects. In general, you can just use it as is, and don't need to poke inside. If you wish to understand deeper, I suggest you look in NHibernate.Generics documentation.

But what about the other side? If I've a user, how do I know what bugs are assigned to it? Well, that turns out to be fairly simple as well, we need to add the following to the User class:

    EntitySet<Bug> _assignedBugs;

 

    [HasMany(typeof(Bug), ColumnKey="AssignedTo",RelationType=RelationType.Set,

        CustomAccess = "NHibernate.Generics.GenericAccessor, NHibernate.Generics")]

    public ICollection<Bug> AssignedBugs

    {

        get { return _assignedBugs; }

    }

 

    public User()

    {

        _assignedBugs = new EntitySet<Bug>(

            delegate(Bug bug) { bug.AssignedTo = this; },

            delegate(Bug bug) { bug.AssignedTo = null; }

            );

    }

What does this mean? Well, here we tell Active Record that a user has many assigned bugs, that it should threat them as a set and how to locate them in the database. Again, we use the NHibernate.Generics addin to get generic collection support. And we define actions that will happen when a bug is assigned to / from the user. The use of NHibernate.Generics add a little of complication to the attributes, but you get generic collection and smart behavior in return, so I think that it is more than worth it. :-)

Okay, that was quite a bit to chew on, let's finish with a couple of notes on the Bugs tables:

CREATE TABLE [dbo].[Bugs](

      [Id] [int] IDENTITY(1,1) NOT NULL,

      [Title] [nvarchar](255) NOT NULL,

      [Text] [nvarchar](max) NOT NULL,

      [Status] [tinyint] NOT NULL,

      [AssignedTo] [int] NOT NULL,

 CONSTRAINT [PK_Bugs] PRIMARY KEY CLUSTERED

(

      [Id] ASC

))

GO

ALTER TABLE [dbo].[Bugs]  WITH CHECK ADD  CONSTRAINT [FK_Bugs_Users] FOREIGN KEY([AssignedTo])

REFERENCES [dbo].[Users] ([Id])

GO

The only thing of note here is the Status column, which is defined as a tinyint, as you recall, a Bug's Status is actually an enum whose backing store is byte, so I persisted it as a tinyint, which has the same size. (Both can have ranges of 0 - 255, which is more than enough for most enums.).

More posts in "Castle Demo App" series:

  1. (03 Mar 2006) ViewComponents, Security, Filters and Friends
  2. (01 Mar 2006) Code Update
  3. (01 Mar 2006) Queries and Foreign Keys
  4. (28 Feb 2006) Complex Interactions
  5. (25 Feb 2006) CRUD Operations on Projects
  6. (22 Feb 2006) Let There Be A New User
  7. (22 Feb 2006) Updating our Users
  8. (20 Feb 2006) Getting serious, the first real page
  9. (20 Feb 2006) MonoRail At A Glance
  10. (20 Feb 2006) The First MonoRail Page
  11. (19 Feb 2006) Many To Many Relations
  12. (19 Feb 2006) Lazy Loading and Scopes
  13. (17 Feb 2006) Active Record Relations
  14. (17 Feb 2006) Getting Started With Active Record