Welcome Guest, you are in: Login

Castle Project

RSS RSS

Navigation (Active Record)





Search the wiki
»

PoweredBy

Getting Started

RSS
Modified on 2011/07/07 18:04 by Gauthier Segay Categorized as Uncategorized
So you've decided to try ActiveRecord but don't know where to start. We can definitely help you on this matter. Just follow the next sections.


A crash course

This tutorial assumes that you have created a simple Console Application to try ActiveRecord (not a windows app, not a web app yet, let's stick with the simplest for now).

We also assume that you know, or at least got a gist of what ActiveRecord is for, so we won't waste your time with fluf.

Necessary assemblies

You must reference the following set of assemblies to use ActiveRecord:
  • Castle.ActiveRecord.dll
  • Castle.Core.dll
  • NHibernate.dll
  • NHibernate.ByteCode.Castle.dll (or the NHibernate ByteCode generator of your choice)
  • log4net.dll
  • Iesi.Collections.dll

Additional assemblies are necessary for further functionality:
  • Validation:
    • Castle.Components.Validation.dll
  • Full Text Search:
    • Lucene.Net.dll
    • NHibernate.Search.dll
  • In-Memory-Testing:
    • System.Data.SQLite.dll
    • nunit.framework.dll (or the testing framework of your choice)

A simple database structure

Now suppose you have the following table structure:
CREATE TABLE Blogs (
    blog_id     int IDENTITY(1, 1) PRIMARY KEY,
    blog_name   varchar(50),
    blog_author varchar(50)
)

CREATE TABLE Posts (
    post_id        int IDENTITY(1, 1) PRIMARY KEY,
    post_title     varchar(50),
    post_contents  text,
    post_category  varchar(50),
    post_blogid    int FOREIGN KEY REFERENCES Blogs (blog_id),
    post_created   datetime,
    post_published bit
)

Creating the classes

Now the fun part begins. Just create a new class named Blog that extends ActiveRecordBase: (You should have imported Castle.ActiveRecord)
public class Blog : ActiveRecordBase<Blog>
{
}
However this is not enough for ActiveRecord to understand what table this class is mapped to, so if you also need to decorate the class with the ActiveRecordAttribute:
[ActiveRecord("Blogs")]
public class Blog : ActiveRecordBase<Blog>
{
}

Note that if the table name was 'Blog', then you wouldn't have to specify the name.

The next step is to specify the primary key (and you must provide a primary key). In this case our primary key is auto generated by the database (column blog_id):
[ActiveRecord("Blogs")]
public class Blog : ActiveRecordBase<Blog>
{
    [PrimaryKey(PrimaryKeyType.Native, "blog_id")]
    public int Id {get; set; }
}
Also, this could be made simpler if you the column was simply 'id':
[PrimaryKey]
public int Id{get; set; }
Finally, map the properties. This couldn't be simpler:
[ActiveRecord("Blogs")]
public class Blog : ActiveRecordBase<Blog>
{
    [PrimaryKey(PrimaryKeyType.Native, "blog_id")]
    public int Id {get; set; }

    [Property("blog_name")]
    public String Name {get; set; }

    [Property("blog_author")]
    public String Author {get; set; }
}
That's it. You're now able to create Blog instances (Create, Update and Delete methods are public and inherited from ActiveRecordBase).

If you didn't inherit from ActiveRecordBase<Blog>, but from the non-generic ActiveRecordBase or a custom base class, you must add more methods in order to query the database, or delete the rows:
public static void DeleteAll()
    {
        DeleteAll( typeof(Blog) );
    }

    public static Blog[] FindAll()
    {
        return (Blog[]) FindAll( typeof(Blog) );
    }

    public static Blog Find(int id)
    {
        return (Blog) FindByPrimaryKey( typeof(Blog), id );
    }

As you see the code is very straightforward. We mapped the class to a table, the fields, and the primary key. Now, before you go and run some test code, we must start the framework properly. In order to do that you must provide some configuration information.

Read more Configuration Reference

Starting the framework

Before you use ActiveRecord, you need to provide the information about which database are you using. You can keep this information in the AppDomain configuration file, or just hardcode it in the application - for the sake of clarity:
// The following requires: using Castle.ActiveRecord.Framework.Config;

InPlaceConfigurationSource source = new InPlaceConfigurationSource();

Hashtable properties = new Hashtable();

properties.Add("hibernate.connection.driver_class", "NHibernate.Driver.SqlClientDriver");
properties.Add("hibernate.dialect", "NHibernate.Dialect.MsSql2000Dialect");
properties.Add("hibernate.connection.provider", "NHibernate.Connection.DriverConnectionProvider");
properties.Add("hibernate.connection.connection_string", "UID=sa;Password=mypass;Initial Catalog=test;Data Source=.");

source.Add( typeof(ActiveRecordBase), properties );

ActiveRecordStarter.Initialize( source, typeof(Blog) );
If you want to use an external configuration file, it will look like the following:

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

<configuration>

    <configSections>
        <section name="activerecord"
type="Castle.ActiveRecord.Framework.Config.ActiveRecordSectionHandler, Castle.ActiveRecord"/>
    </configSections>

    <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="UID=sa;Password=yourpass;Initial Catalog=test;Data Source=." />
      </config>

    </activerecord>

</configuration>

In this case, you can just initialize like this:

IConfigurationSource source = System.Configuration.ConfigurationManager.GetSection("activerecord") as IConfigurationSource;
ActiveRecordStarter.Initialize( source, typeof(Blog) );

Important: you need add Post class in ActiveRecordStarter.Initialize

You can then perform your operations with the objects:
Blog.DeleteAll();

Blog blog = new Blog();
.. set fields ..
blog.Save(); // or blog.Create();
... more operations ...
blog.Save(); // or blog.Update();
... tired of this blog?
blog.Delete();

For more on mappings see Mappings

Adding relations mapping

One of the beauties of having a DomainModel implemented using ActiveRecord is how it makes simple to create interelations within your object model. In this case we have created a Blog class, and it's fair enough to provide a Post class and the relation between them. In plain english the relation is
  • A Post belongs to a Blog
  • A Blog has many Posts

Using HasManyAttribute and BelongsToAttribute

To express these relations you might use these attributes. For example:
[ActiveRecord("Blogs")]
public class Blog : ActiveRecordBase
{
...
    private List<Post> _posts;

...

    [HasMany(typeof(Post), Table="posts", ColumnKey="post_blogid")]
    public IList<Post> Posts
    {
        get { return _posts; }
        set { _posts = value; }
    }
}

[ActiveRecord("Posts")]
public class Post : ActiveRecordBase
{
...
    private Blog _blog;
...
    [BelongsTo("post_blogid")]
    public Blog Blog
    {
        get { return _blog; }
        set { _blog = value; }
    }
}

In this case, as the Post class has a BelongsTo association, ActiveRecord can collect the information to create the correct HasMany, so you really don't need to specify all information:

[ActiveRecord("Blogs")]
public class Blog : ActiveRecordBase
{
...
    private List<Post> _posts;
...
    [HasMany]
    public IList<Post> Posts
    {
        get { return _posts; }
        set { _posts = value; }
    }
}

If you're wondering, this is how the Post class should look like:
[ActiveRecord("Posts")]
public class Post : ActiveRecordBase<Post>
{
    public Post()
    {
        Created = DateTime.Now;
    }

    public Post(String title) : this()
    {
        Title = title;
    }


    public Post(Blog blog, String title, String contents, String category) : this()
    {
        Blog = blog;
        Title = title;
        Contents = contents;
        Category = category;
    }

    [PrimaryKey(PrimaryKeyType.Native, "post_id")]
    public int Id {get; set; }

    [Property("post_title")]
    public String Title {get; set; }

    [Property("post_contents",ColumnType="StringClob")]
    public String Contents {get; set; }

    [Property("post_category")]
    public String Category {get; set; }

    [BelongsTo("post_blogid")]
    public Blog Blog {get; set; }

    [Property("post_created")]
    public DateTime Created {get; set; }

    [Property("post_published")]
    public bool Published {get; set; }

Using the relation

A big source of confusion is how to use the relation (at least with the cascading defaults), for example, the following code is not going to work

Blog blog = Blog.Find(1);

blog.Posts.Add( new Post("This is my first post") );

blog.Save(); // Exception!

That's because the Post instance in this case would be a transient class, not persisted. It must be persisted first. So this is the correct code:

Blog blog = Blog.Find(1);

Post post = new Post("This is my first post");
post.Save(); // Saving it first

blog.Posts.Add( post );

blog.Save(); // Now it's OK

You can also associate the Post with the blog using the other side of the relation:
Blog blog = Blog.Find(1);

Post post = new Post("This is my first post");
post.Blog = blog; // Linking them
post.Save();

But of course, don't assume that when you do that the blog.Posts will automatically be notified and will then have one element, you must "refresh" it if you want it to reflect the latest changes.

You can also change the relation cascading settings (on the Blog class):

[HasMany(typeof(Post), Table="Posts", ColumnKey="post_blogid", Cascade=ManyRelationCascadeEnum.SaveUpdate)]
public IList<Post> Posts
{
    get { return _posts; }
    set { _posts = value; }
}

On this case the following code is going to work:

Blog blog = Blog.Find(1);

blog.Posts.Add( new Post("This is my first post") );

blog.Save(); // Ok

Finding records

The following methods are exposed by ActiveRecordBase<Blog>, but not by the non-generic version of the base class. In case you do not directly inherit from ActiveRecordBase<Blog>, you have to provide the methods yourself, which wont be nothing more than delegating to ActiveRecordBase protected methods.

FindByPrimaryKey

A Find method will usually be implemented like this:

public static Blog Find(int id)
{
    return (Blog) FindByPrimaryKey(typeof(Blog), id);
}

In this case, if the record is not found an exception will be the thrown. If you dont want that, and expected only a null return, then change to that:


public static Blog Find(int id)
{
    return (Blog) FindByPrimaryKey(typeof(Blog), id, false);
}

FindAll

The FindAll allows you to defined an order by and a criteria, which can go from simple to complex: For most cases, instead of using FindAll you can go with FindAllByProperty:


public static Blog[] FindByName(String name)
{
    return (Blog[]) FindAllByProperty(typeof(Blog), "Name", name);
}

Please note that we're using a property name, not a column name.

If you just want all records:

public static Blog[] FindAll()
{
    return (Blog[] FindAll(typeof(Blog));
}

With a criteria (You should have imported NHibernate.Expression):


public static Blog[] FindByAuthor(String author)
{
    return (Blog[]) FindAll(typeof(Blog), Expression.Eq("Author", author));
}

With an order and criteria:


public static Blog[] FindByAuthor(String author)
{
    return (Blog[]) FindAll(typeof(Blog), Expression.Eq("Author", author));
}

FindFirst and FindOne

Sometimes you want the first record found the a criteria, and sometimes you're expecting only one record or none to exists on the database. In those cases use FindFirst or FindOne:

public static Blog FindFirstBlogByAuthor(String author)
{
    return (Blog) FindFirst(typeof(Blog), Expression.Eq("Author", author));
}

Batching changes

If you know NHibernate, you know that every set of operation must be enclosed within a valid session. ActiveRecord encapsulates and even hides operations with the session, but even so you can gain access to it (see Using HQL) and more importantly, batch operations to be performed together. Using an earlier example:


using(new SessionScope())
{
  Blog blog = Blog.Find(1);
  Post post = new Post("This is my first post");
  post.Save();

  blog.Posts.Add( post );

  blog.Save();
} // The changes will be sent to the DB when the session is disposed here

Final thoughts

This was an introductory tutorial. There are plenty to be said about ActiveRecord and we encourage you to try, bend and push it. In a few hours you'll probably have mastered it.

ScrewTurn Wiki version 3.0.4.560. Some of the icons created by FamFamFam.