Castle First Example

Getting started with ActiveRecord

Castle ActiveRecord is a powerful tool and allows you do many kinds of relations easily. Change the classes to match a change in the database schema is a breeze. It is an important tool for people doing agile development.

As Castle ActiveRecord is built on top of NHibernate, it is also important that you know how it works and always consult its documentation, especially when doing HQL queries.

This Getting started will focus on the construction of a Desktop application (using Winforms) that manages blogs and its posts. You will learn

  • How to create ActiveRecord types
  • How to configure and initialize ActiveRecord
  • How to add methods to your classes to perform searches

You can also download the complete example:

InitializingAndConfiguring.zip (15.81k)

Proceed with Requirements.

Screenshots

Requirements

We assume that you have Castle Project assemblies and the NHibernate assemblies that comes with it. If you don't, we encourage you to download the MSI installer distribution, as it makes the assemblies visible to Visual Studio.

This Getting started assumes that you are using MSSQL Server 2003, but any other database supported by NHibernate will do.

If you are using Visual Studio .Net, add references to the following assemblies:

  • Castle.ActiveRecord.dll
  • Castle.Core.dll
  • Castle.DynamicProxy.dll
  • Iesi.Collections.dll
  • log4net.dll
  • NHibernate.dll

If you are using another environment or compiling with NAnt, make sure assemblies above are referenced from the Castle installation directory.

Creating the database and tables

We will use three tables: User, Blog and Post. We also use a database called test. You can create the tables using the schema below or you can let ActiveRecord generate the schema for you.

As you can see the User table has no relation with the others. It is going to be used only for controlling access to the application. Meanwhile the Blog and Post are related. A blog can have many posts, hence the relation is blog is one-to-many and on the post is many-to-one.

 
CREATE TABLE Blog 
(
    [Id] [int] IDENTITY (1, 1) NOT NULL,
    [Name] [varchar] (25) NOT NULL,
    [Author] [varchar] (25) NOT NULL 
) ON [PRIMARY]
 
CREATE TABLE Post 
(
    [Id] [int] IDENTITY (1, 1) NOT NULL,
    [BlogId] [int] NOT NULL,
    [Title] [varchar] (100) NOT NULL,
    [Contents] [text] NOT NULL,
    [Category] [varchar] (25) NULL,
    [Created] [datetime] NOT NULL,
    [Published] [bit] NOT NULL 
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
 
CREATE TABLE User (
    [Id] [int] IDENTITY (1, 1) NOT NULL,
    [UserName] [varchar] (25) NOT NULL,
    [Password] [varchar] (15) NOT NULL 
) ON [PRIMARY]
 
ALTER TABLE Blog WITH NOCHECK ADD 
CONSTRAINT [PK_Blog] PRIMARY KEY CLUSTERED ( [Id] ) ON [PRIMARY] 
 
ALTER TABLE Post WITH NOCHECK ADD 
CONSTRAINT [PK_Post] PRIMARY KEY CLUSTERED ( [Id] ) ON [PRIMARY] 
 
ALTER TABLE User WITH NOCHECK ADD 
CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED ( [Id] ) ON [PRIMARY] 

Creating the classes

Now it is time to create the classes that will map to the tables, and here you will see how easy it is. First let's start with the simplest one, the User class:

Creating the User class

1.    Create a empty class named User and add the using statements:

2.            
3.           namespace BlogSample
4.           {
5.               using System;
6.               using Castle.ActiveRecord;
7.            
8.               public class User
9.               {
10.           }
11.       }

12.Now make the class extend from ActiveRecordBase and use tha ActiveRecordAttribute:

13.        
14.       namespace BlogSample
15.       {
16.           using System;
17.           using Castle.ActiveRecord;
18.        
19.           [ActiveRecord]
20.           public class User : ActiveRecordBase
21.           {
22.           }
23.       }

24.Add the fields and the corresponding properties for the columns on the database:

25.        
26.       namespace BlogSample
27.       {
28.           using System;
29.           using Castle.ActiveRecord;
30.        
31.           [ActiveRecord]
32.           public class User : ActiveRecordBase
33.           {
34.               private int id;
35.               private string username;
36.               private string password;
37.        
38.               public int Id
39.               {
40.                   get { return id; }
41.                   set { id = value; }
42.               }
43.        
44.               public string Username
45.               {
46.                   get { return username; }
47.                   set { username = value; }
48.               }
49.        
50.               public string Password
51.               {
52.                   get { return password; }
53.                   set { password = value; }
54.               }
55.           }
56.       }

57.Finally add the attributes that inform ActiveRecord what the properties are (ie simple properties, primary keys and relationships). In this case we just need to mark the Id as the primary key and the rest as simple properties:

58.        
59.       namespace BlogSample
60.       {
61.           using System;
62.           using Castle.ActiveRecord;
63.        
64.           [ActiveRecord]
65.           public class User : ActiveRecordBase
66.           {
67.               private int id;
68.               private string username;
69.               private string password;
70.        
71.               [PrimaryKey]
72.               public int Id
73.               {
74.                   get { return id; }
75.                   set { id = value; }
76.               }
77.        
78.               [Property]
79.               public string Username
80.               {
81.                   get { return username; }
82.                   set { username = value; }
83.               }
84.        
85.               [Property]
86.               public string Password
87.               {
88.                   get { return password; }
89.                   set { password = value; }
90.               }
91.           }
92.       }

That is it. Now we need to create the classes for the Blog and Post tables. The process is the same and we encourage you to try yourself.

Creating the Blog class

The Blog class is very straighforward. Bear in mind that it is not complete. We still need to create a link between it and the Post. We will do that shortly.

 
namespace BlogSample
{
    using System;
    using System.Collections;
 
    using Castle.ActiveRecord;
 
    [ActiveRecord]
    public class Blog : ActiveRecordBase
    {
        private int id;
        private String name;
        private String author;
 
        public Blog()
        {
        }
 
        [PrimaryKey]
        public int Id
        {
            get { return id; }
            set { id = value; }
        }
 
        [Property]
        public String Name
        {
            get { return name; }
            set { name = value; }
        }
 
        [Property]
        public String Author
        {
            get { return author; }
            set { author = value; }
        }
    }
}

Creating the Post class

The Post class is also simple, but it is not completed as well, we need to link it with the Blog class.

You should also note something different. The Contents property uses [Property(ColumnType="StringClob")]. This is requires as this property is bound to a text column.

 
namespace BlogSample
{
    using System;
 
    using Castle.ActiveRecord;
 
 
    [ActiveRecord]
    public class Post : ActiveRecordBase
    {
        private int id;
        private String title;
        private String contents;
        private String category;
        private DateTime created;
        private bool published;
 
        public Post()
        {
            created = DateTime.Now;
        }
 
        [PrimaryKey]
        public int Id
        {
            get { return id; }
            set { id = value; }
        }
 
        [Property]
        public String Title
        {
            get { return title; }
            set { title = value; }
        }
 
        [Property(ColumnType="StringClob")]
        public String Contents
        {
            get { return contents; }
            set { contents = value; }
        }
 
        [Property]
        public String Category
        {
            get { return category; }
            set { category = value; }
        }
 
        [Property]
        public DateTime Created
        {
            get { return created; }
            set { created = value; }
        }
    }
}

You probably agree that was fairly easy, even if this is the first you have used ActiveRecord. Adding relationshipts will not be much more difficult.

Adding the relationships

Now it is time to tell ActiveRecord that the classes Blog and Post have a relationship.

As stated before the relationships are one-to-many and many-to-one. For the former we will use the HasManyAttribute, and for the latter we will use the BelongsToAttribute.

Using the BelongsTo on the Post class

1.    We need to include a field and a property on the Post class to hold the Blog instance that - semantically - owns the Post.

2.            
3.               [ActiveRecord]
4.               public class Post : ActiveRecordBase
5.               {
6.                   ...
7.                   
8.                   private Blog blog;
9.                   
10.               ...
11.        
12.               public Blog Blog
13.               {
14.                   get { return blog; }
15.                   set { blog = value; }
16.               }
17.        
    

18.Finally add the BelongsToAttribute to the property to tell ActiveRecord that this is a many-to-one relationship. Note that by default the column name is the property name. In our case the column name is BlogId so we need to inform that explicitly to not rely on the default.

19.        
20.       [BelongsTo("BlogId")]
21.       public Blog Blog
22.       {
23.           get { return blog; }
24.           set { blog = value; }
25.       }
    

That was very simple. Now let's see the other side of the relation.

Using the HasMany on the Blog class

1.    Seamlessly, we need to include a field and a property on the Blog class to hold all Post instances that - semantically - it has.

2.            
3.               using System;
4.               using System.Collections;
5.            
6.               using Castle.ActiveRecord;
7.            
8.               [ActiveRecord]
9.               public class Blog : ActiveRecordBase
10.           {
11.               ...
12.               
13.               private IList posts = new ArrayList();
14.               
15.               ...
16.        
17.               public IList Posts
18.               {
19.                   get { return posts; }
20.                   set { posts = value; }
21.               }
22.        
    

23.Add the HasManyAttribute to the property to tell ActiveRecord that this is a one-to-many relationship:

24.        
25.       [HasMany(typeof(Post))]
26.       public IList Posts
27.       {
28.           get { return posts; }
29.           set { posts = value; }
30.       }
    

The HasManyAttribute has a few useful attributes that should be used to have more control over the relationship behavior. For example: this is a bidirectional relationship as both classes have the relations. What side should "control" the relation? Do you intend to create a relation between a Blog and a Post by adding a post to the blog.Posts collection or use the Post.Blog property? I would say that using Post.Blog is preferable in our scenario. The posts collection is informative, and should be read only. In order to do that, use the Inverse property:

 
[HasMany(typeof(Post), Inverse=true)]
public IList Posts
{
    get { return posts; }
    set { posts = value; }
}
    

You might also want to define the cascade behavior. What should NHibernate do with the posts when you delete the blog that owns them (for example)? Let's say that it must delete all:

 
[HasMany(typeof(Post), Inverse=true, Cascade=ManyRelationCascadeEnum.AllDeleteOrphan)]
public IList Posts
{
    get { return posts; }
    set { posts = value; }
}
    

Another thing you should know: Castle ActiveRecord will infer the table and the column that has the relation on the other side. It is capable of doing that because the other side (read the Post class) has a BelongsToAttribute that points back the the Blog. If this is not the case, then you would have to include the table and column information, and it would look like the following:

 
[HasMany(typeof(Post), 
    Table="Posts", ColumnKey="blogid", 
    Inverse=true, Cascade=ManyRelationCascadeEnum.AllDeleteOrphan)]
public IList Posts
{
    get { return posts; }
    set { posts = value; }
}

Now it is time to start the framework and start playing with what we have coded so far.

Initializing the framework

Castle ActiveRecord must be initialized before you use any class. This should be done only once and preferably at application start up. In our case, as our example is a WinForms application, we would initialize ActiveRecord before show anyform.

ActiveRecord also needs a small configuration to tell it which database you are using and how to speak to it. In fact this is NHibernate configuration that ActiveRecord pass on when it initializes NHibernate. So it is time to decide where you want to keep its configuration.

Configuring ActiveRecord

We have some option on how to configure ActiveRecord and where to keep the configuration. Those are detailed explained on the documentation. For simplicity's sake we would use a simple standalone xml file:

 

<?xml version="1.0" encoding="utf-8" ?>

 

<activerecord>

 

  <config>

    <add

        key="hibernate.connection.driver_class"       

        value="NHibernate.Driver.SqlClientDriver" />

    <add

        key="hibernate.dialect"                       

        value="NHibernate.Dialect.MsSql2000Dialect" />

    <add

        key="hibernate.connection.provider"           

        value="NHibernate.Connection.DriverConnectionProvider" />

    <add

        key="hibernate.connection.connection_string"   

        value="Data Source=.;Initial Catalog=test;Integrated Security=SSPI" />

  </config>

 

</activerecord>

Quick Note

The configuration above uses SQL Server 2000. If you are using a different database you need to change the dialect. Please refer to the Xml Configuration Reference document for more information.

Now save the file (on the folder to where the assembly is generated) with the content above as appconfig.xml. The following code creates a XmlConfigurationSource that loads the configuration above:

 

namespace BlogSample

{

    using System.Windows.Forms;

 

    using BlogSample.UI;

 

    using Castle.ActiveRecord;

    using Castle.ActiveRecord.Framework.Config;

 

    public class App

    {

        public static void Main()

        {

            XmlConfigurationSource source = new XmlConfigurationSource("appconfig.xml");

        }

    }

}

Initializing ActiveRecord

Finally we will initialize ActiveRecord passing the configuration loaded. We also need to specify the types ActiveRecord should inspect. We can do this by specifying an assemblies, an array of assemblies or an array of types. For simplicity's sake, let's use an array of types:

 

namespace BlogSample

{

    using System.Windows.Forms;

 

    using BlogSample.UI;

 

    using Castle.ActiveRecord;

    using Castle.ActiveRecord.Framework.Config;

 

    public class App

    {

        public static void Main()

        {

            XmlConfigurationSource source = new XmlConfigurationSource("appconfig.xml");

           

            ActiveRecordStarter.Initialize( source, typeof(Blog), typeof(Post), typeof(User) );

        }

    }

}

At this point we are ready to use our type (as long as the configuration is right, the database exists).

Creating the tables

You may let ActiveRecord create the tables if you have not created it:

 

public class App

{

    public static void Main()

    {

        XmlConfigurationSource source = new XmlConfigurationSource("appconfig.xml");

       

        ActiveRecordStarter.Initialize( source, typeof(Blog), typeof(Post), typeof(User) );

 

        if (MessageBox.Show("Do you want to let ActiveRecord create the database tables?",

                              "Schema", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)

        {

            ActiveRecordStarter.CreateSchema();

        }

    }

}

Now that our hands are partially dirty, we can start our real code that use our classes. We would need to include more methods on our classes to perform more database related operations.

Using the classes

If you are now acquainted with most parts of ActiveRecord, we just need to start using the classes we have created. You will note that it couldn't get more natural.

Creating the first user

Our application has a login form. You must inform a correct login and password in order to get access to the rest of the application. But the database was just created. What about adding the first user?

The following code creates an user, it should be added to the main class just below the initialization.

 
User user = new User("admin", "123");
 
user.Create();    

What if you run the application multiples times? A good solution would be to create the user only if no user is found on the database. We could count the existing users and if it is zero, we create the admin:

 
if (User.GetUsersCount() == 0)
{
    User user = new User("admin", "123");
 
    user.Create();
}

Obviously we need to add the method GetUsersCount to the User class:

 
[ActiveRecord("[User]")]
public class User : ActiveRecordBase
{
    ...
    
    public static int GetUsersCount()
    {
        return CountAll(typeof(User));
    }
}

The login form

This window ask for a login:

The code is very simple, and makes uses of a search method that so far does not exists:

 
private void logInButton_Click(object sender, System.EventArgs e)
{
    User user = User.FindByUserName(loginText.Text);
 
    if (user == null)
    {
        MessageBox.Show(this, "User not found", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        return;
    }
 
    if (user.Password != passwordText.Text)
    {
        MessageBox.Show(this, "Wrong password", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        return;
    }
 
    DialogResult = DialogResult.OK;
    Hide();
}

To make this work we need to include the method FindByUserName. As usual it is not difficult at all:

 
using NHibernate.Expression;
 
[ActiveRecord("[User]")]
public class User : ActiveRecordBase
{
    ...
    
    public static User FindByUserName(string userName)
    {
        // Note that we use the property name, _not_ the column name
        return (User) FindOne(typeof(User), Expression.Eq("Username", userName));
    }
}

The blog management

This window allows one to add, edit and delete a blog. It also brings another window to manage the posts that belongs to the selected blog.

When you click Add... or select an existing blog, another window will show.

To populate the blog list we select all existing blogs:

 
private void PopulateBlogList()
{
    blogsList.Items.Clear();
 
    foreach(Blog blog in Blog.FindAll())
    {
        ListViewItem item = blogsList.Items.Add(blog.Id.ToString());
 
        item.Tag = blog;
 
        item.SubItems.Add(blog.Name);
        item.SubItems.Add(blog.Author);
    }
}

We need to code the FindAll method on the Blog class to make this work. Let's also add a method to search for the primary key:

 
[ActiveRecord]
public class Blog : ActiveRecordBase
{
    ...
    
    public static Blog[] FindAll()
    {
        return (Blog[]) ActiveRecordBase.FindAll(typeof(Blog));
    }
 
    public static Blog Find(int id)
    {
        return (Blog) ActiveRecordBase.FindByPrimaryKey(typeof(Blog), id);
    }
    
    ...

To create a new blog on the database all we need to do is

 
Blog blog = new Blog();
blog.Name = "My blog";
blog.Author = "hammett";
blog.Create();
    

Suppose that you don't have an blog instance, but you know the id. Let's change a blog instance then:

 
Blog blog = Blog.Find(100); // Id that we know exists
blog.Name = "Different name";
blog.Author = "Different author";
blog.Update();
    

To remove an instance, just call the Delete method.

The posts management

With the posts we just cannot forget to relate the blog instance with the post we are creating:

 
currentPost.Blog = parentBlog;
 
currentPost.Title = titleText.Text;
currentPost.Contents = contentsText.Text;
currentPost.Category = categoryText.Text;
currentPost.Created = createdDtTime.Value;
currentPost.Published = publishedCheck.Checked;
 
currentPost.Save();

Getting More

There are lots of operations and possibilities exposed by ActiveRecord. We have just show you 1% of all it can do. Now you should play with it a little and start exploring the documentation to learn more about mappings, relations, tunnings, SessionScopes and lazy collections.

posted on 2006-11-03 10:40  心悦  阅读(531)  评论(0编辑  收藏  举报