Home

Castle Stronghold

Table of contents

Generics support

If you happen to be using .Net Framework 2.0, you might benefit from the generic version of base classes.

It is also possible to use typed collections instead of the IList or ISet

ActiveRecordBase<T>

When you inherit from ActiveRecordBase most of the method that you have to implement if you were using ActiveRecordBase will be strongly typed and available as public methods. For example, given the following ActiveRecord type definition:


using Castle.ActiveRecord;

[ActiveRecord]
public class Customer : ActiveRecordBase<Customer>
{
    // Mapping omitted as it is the same
}

We can use several operations without the need to implement them.


// Find all
Customer[] customers = Customer.FindAll();

// Find by property
customers = Customer.FindAllByProperty("Name", "John doe");

// Find by primary key
Customer cust = Customer.Find( 1 );

ActiveRecordValidationBase<T>

Additionally, a version of ActiveRecordValidationBase is also available. More information about it on Validation Support.

Typed collections

If you want to use the strongly typed, generic versions of the collections, you'll need a separate extension, called NHibernate.Generics. This extension was developed by our fellow Castle member Ayende Rahien, and it can be found at his web site.

This article is about combining the use of NHibernate.Generics and ActiveRecord to build a domain model that will be natural for programmers in .NET 2.0.

Quick Note

NHibernate.Generics depends on NHibernate 1.0.2, which is not supported by Castle ActiveRecord versions prior to RC1.

Let's take the following sample tables:


CREATE TABLE [dbo].[Blogs] (
    [blog_id] [int] IDENTITY (1, 1) NOT NULL ,
    [blog_name] [varchar] (50) NULL ,
    [blog_author] [varchar] (50) NULL 
) ON [PRIMARY]

CREATE TABLE [dbo].[Posts] (
    [post_id] [int] IDENTITY (1, 1) NOT NULL ,
    [post_title] [varchar] (50) NULL ,
    [post_contents] [text] NULL ,
    [post_category] [varchar] (50) NULL ,
    [post_blogid] [int] NULL ,
    [post_created] [datetime] NULL ,
    [post_published] [bit] NULL 
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

The domain model that we want is a Blog class that holds a collection of Posts, and a Post class that holds a reference to its parent Blog.

First things first, let's implement the Blog class:


[ActiveRecord]
public class Blog : ActiveRecordBase<Blog>
{
    private EntitySet<Post> _posts;
    private int _id;
    private string _name;
    private string _author;
    
    public Blog()
    {
        _posts = new EntitySet<Post>(
                delegate(Post p) { p.Blog = this; },
                delegate(Post p) { p.Blog = null; }
            );
    }
    
    public Blog(string name, string author) : this()
    {
        this._name = name;
        this._author = author;
    }
    
    [PrimaryKey(PrimaryKeyType.Native, "blog_id", Access=PropertyAccess.NoSetterCamelCaseUnderscore)]
    private int Id
    {
        get { return _id; }
    }
    
    [Property("blog_name")]
    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }
    
    [Property("blog_author")]
    public string Author
    {
        get { return _author; }
        set { _author = value; }
    }

    [HasMany(typeof (Post), "post_blogId", "Posts", Inverse=true,
        CustomAccess = Generics.Access,RelationType = RelationType.Set)]
    public ICollection<Post> Posts
    {
        get { return  _posts; }
    }
        
}
Quick Note

You must initialize the collection instance when using custom accessors like the example above.

What we have defined so far:

Warning

The property name must be same as the field name. Otherwise an exception will happen.

The Posts collections deserves a special attention. The attribute decorating it is a complex one, so let's take it one piece at a time:

Now that we've taken the attribute apart, all we need to understand is that this lengthy attribute is telling ActiveRecord: this is a generic collection of Posts.

Now let's take a look at the constructor, we are doing something very strange there.


_posts = new EntitySet<Post>{
        delegate(Post p) { p.Blog = this; },
        delegate(Post p) { p.Blog = null; }
    );

We are creating a new EntitySet of Posts and telling it how to maintain the relation between a Blog and a Post. We are using the C# 2.0's anonymous delegate feature, which allows us to specify it very cleanly.

And that is all you need to do. The relation is created automatically at this point. These definitions allow you to use the following:


Blog blog = Blog.Load(1);
Post post = new Post("First Post", "My First Post", "Slashdot");
blog.Posts.Add(post);
post.Save();

Now let's take a look at the Post class:


[ActiveRecord]
public class Post : ActiveRecordBase<Post>
{
    private int _id;
    private string _title;
    private string _content;
    private string _category;
    private DateTime _created;
    private bool _published;
    private EntityRef<Blog> _blog;
    
    public Post()
    {
        _blog = new EntityRef<Blog>(
                delegate(Tests.Blog b) { b.Posts.Add(this); },
                delegate(Tests.Blog b) { b.Posts.Remove(this); }
            );    
    }
    
    public Post(string title, string content, string category):this()
    {
        this._title = title;
        this._content = content;
        this._category = category;
        this._created = DateTime.Now;
        this._published = false;
    }
    
    [PrimaryKey(PrimaryKeyType.Native,"post_id", Access=PropertyAccess.NoSetterCamelCaseUnderscore)]
    private int Id
    {
        get { return _id; }
    }
    
    [Property("post_title")]
    public string Title
    {
        get { return _title; }
        set { _title = value; }
    }
    
    [Property("post_content")]
    public string Content
    {
        get { return _content; }
        set { _content = value; }
    }
    
    [Property("post_category")]
    public string Category
    {
        get { return _category; }
        set { _category = value; }
    }
    
    [Property("post_created", Access=PropertyAccess.NoSetterCamelCaseUnderscore)]
    public DateTime Created
    {
        get { return _created; }
    }
    
    [Property("post_published")]
    public bool Published
    {
        get { return _published; }
        set { _published = value; }
    }
    
    [BelongsTo("post_blog_id", CustomAccess=Generics.Access)]
    public Blog Blog
    {
        get { return _blog.Value; }
        set { _blog.Value = value; }
    }
    
}

Most of the definitions here are straightforward, so I'll skip them. Now we get to the Blog property, and you can see that we defined an EntityRef<> object to holds it. The reason for that is that you need to maintain the relationship on this side as well, so we again tell ActiveRecord to use custom access strategy.

Then, in the constructor, we are creating the EntityRef<> and instructing it how to create a connection when it is assigned.


_blog = new EntityRef<Blog>(
    delegate(Tests.Blog b) { b.Posts.Add(this); },
    delegate(Tests.Blog b) { b.Posts.Remove(this); }
);

Another thing that we need to pay attention to is the Blog property.


    get { return _blog.Value; }
    set { _blog.Value = value; }

We are not using the _blog field directly, rather we are getting and setting on its Value property. This allows the EntityRef to handle the relationships correctly.

That is all you need to do to add generics capabilities to your domain model.

Google
Search WWW Search castleproject.org