Geeks With Blogs

News
www.flickr.com
This is a Flickr badge showing public photos from hammett1. Make your own badge here.

hamilton *hammett* verissimo Another special person... just like everyone else

On this second part of the series of articles I’ll focus on the framework initialization. And yes, I intend to dive into the details and inner workings.

Getting your hands dirty

To use ActiveRecord you just need to:

  • Decorate the classes that represent a database entity
  • Start the framework specifying the mapping types (or an assembly, or multiple assemblies) and a configuration source

There will be an article just to go through every possible mapping and relation types. For this one I'll focus on the most common usage. Make sure you also check the official crash course.

So here's the 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
)

And the ActiveRecord classes:

[ActiveRecord("Blogs")]
public class Blog : ActiveRecordBase
{
    private int _id;
    private String _name;
    private String _author;
    private IList _posts;

    public Blog()
    {
    }

    public Blog(String name)
    {
        _name = name;
    }

    [PrimaryKey(PrimaryKeyType.Native)]
    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; }
    }

    [HasMany(typeof(Post))]
    public IList Posts
    {
        get { return _posts; }
        set { _posts = value; }
    }

    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 );
    }
}

[ActiveRecord("Posts")]
public class Post : ActiveRecordBase
{
    private int _id;
    private String _title;
    private String _contents;
    private String _category;
    private DateTime _created;
    private bool _published;
    private Blog _blog;

    public Post()
    {
        _created = DateTime.Now;
    }

    public Post(Blog blog, String title, String contents, String category) : this()
    {
        _blog = blog;
        _title = title;
        _contents = contents;
        _category = category;
    }

    [PrimaryKey(PrimaryKeyType.Native)]
    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; }
    }

    [BelongsTo("blogid")]
    public Blog Blog
    {
        get { return _blog; }
        set { _blog = value; }
    }

    [Property("created")]
    public DateTime Created
    {
        get { return _created; }
        set { _created = value; }
    }

    [Property("published")]
    public bool Published
    {
        get { return _published; }
        set { _published = value; }
    }

    public static void DeleteAll()
    {
        DeleteAll( typeof(Post) );
    }

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

No fancy stuff. Yet.

The configuration

What goes on the configuration?, you may ask. The usual: connection string, the database driver. These configurations are pretty much NHibernate configurations.

There are some configuration source flavors that you'll pick depending on what you're doing. If you're just playing with ActiveRecord, use the InPlaceConfigurationSource. On a real application you may want to externalize the configuration using the ActiveRecordSectionHandler or to just read from a standalone xml file use XmlConfigurationSource. You can create your own source by implementing the interface IConfigurationSource.

So, sticking with the simple, let’s use the 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", "Data Source=.;Initial Catalog=test;Integrated Security=SSPI");

InPlaceConfigurationSource source = new InPlaceConfigurationSource();
source.Add(typeof(ActiveRecordBase), properties);

With this code we’re ready to access a MS SqlServer database named test. You may have noticed that we add the configuration to a type, in our case it was ActiveRecordBase. I’ll explain why later in this article.

Starting the framework

So the configuration is ready, let’s start ActiveRecord using one of the overloads of the Initialize method:

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

The Initialize method should be called only once, as it makes no sense calling several times. It must be called before you start using the types, otherwise operations will fail with a message like “An ActiveRecord class was used but the framework seems not properly initialized. Did you forget about ActiveRecordStarter.Initialize()?”. Can it be clearer than that? :-) For web applications, consider putting the invocation of the Initialize method on Application_Start.

Now what happens when you invoke the Initialize? Well, lots of things. This is probably the most expensive ActiveRecord call and you ought to know why.

The initialization process

ActiveRecord's initialization process consists of several steps:

  • Initializing internal components
  • Setting up a NHibernate's Configuration instance per model hierarchy
  • Creating an ActiveRecordModel for each "ActiveRecord type" (any ordinary class that fulfils the requirements to be considered a mapping class, in other words a class that declares the ActiveRecordAttribute)
    • The model is populated with the information extracted from the class' attributes
  • Running a sequence of visitors
    • The first visitor connects models, which you might think as graph vertexes being connected, although there's no special information on the edges
    • The second visitor checks the semantics of the mapping declared by you. It basically searches for errors while inferring the eventual missing information. If one is found, it presents a big friendly exception message
    • The final visitor assumes that everything is OK and outputs the NHibernate xml mapping
  • At this point ActiveRecord is ready for the battle

Now let's analyze each step carefully:

Initializing internal components

There are two main players on ActiveRecord’s internals. The implementation of ISessionFactoryHolder, and the implementation of IThreadScopeInfo. The former is responsible to acquire a session from the right place when requested, and is type hierarchy aware (more about that in a second). The latter is responsible to bind the scope information to, well, somewhere that represents one activity. For ordinary application this would be the TLS data slots, for web application a different implementation of IThreadScopeInfo uses the HttpContext.Current.Items.

You can provide your own implementations if you want to. Just check the configuration reference in order to know how to specify new types.

We can say that 100% of ActiveRecord operations will interact with the ISessionFactoryHolder. That’s cause most operations requires a NHibernate’s ISession implementation and the ISessionFactoryHolder knows how to get one. The logic is pretty trivial: is there any active scope on this “logical activity” – it asks the IThreadScopeInfo to get – if not, it just opens a session and returns it. Otherwise it will ask the scope to provide the session.

By the way, scopes demand a separated article.

Setting up a NHibernate's Configuration instance per model hierarchy

Usually you will use ActiveRecord to access one – and only one – database. But what if you want to access more than one? Remember that we associated the configuration information with the ActiveRecordBase type? For ActiveRecord that means that any type that extends ActiveRecordBase will use that database configuration. To access a different database you’ll need to create an abstract class that extends ActiveRecordBase and configure it properly. Types that maps tables from this other database needs to extend your abstract type.

When ActiveRecord initializes it creates Configuration and associates with these base types, that I like to call root types. Later, when your Blog class asks for a session, ActiveRecord gets its root type, obtains the ISessionFactory associated with it and caches the association (type to ISessionFactory instance), and finally returns the session.

Creating an ActiveRecordModel for each "ActiveRecord type"

ActiveRecordModel is a graph node. It has information about a possible parent (used when you use type mapping hierarchy through discriminators or joined subclasses); all mapped fields, which are represented by a FieldModel; all mapped properties, which are represented by PropertyModel; and the same thing for the primary key and relations.

In an old implementation we used to work directly on the types, but it turned out that always querying the attributes was very expensive. With this new implementation we collect everything at once. The responsible for that is the ActiveRecordModelBuilder.

You may also note that all models are visitable:

public void Accept(IVisitor visitor)
{
    visitor.VisitModel(this);
}

The visitor pattern is a life saver as it keeps you from checking for different types to act accordingly. But I have the feeling that most developers just don’t get it, which is a shame.

But I digress. ActiveRecordModelBuilder will be invoked for the types that you passed to the Initialize method on ActiveRecordStarter (or the set of types if you’ve specified one or more assemblies). At this point ActiveRecordStarter discards any type that does not have the ActiveRecordAttribute defined. So remember this if things are not working as expected :-)

There are exceptions though. Nested classes (those that NHibernate likes to call Components for some obscure reason) don’t need this attribute.

“What about abstract classes, should I put an ActiveRecordAttribute there too?” Depends on what you’re doing. In fact you should check this test case for clarification.

Running a sequence of visitors

At this point we have a collection of populated ActiveRecordModels. It’s time to act on them.

The first visitor connects models

The visitor is called GraphConnectorVisitor which explains all it does. However it only needs to collect the types that are using type mapping hierarchy through discriminators or joined subclasses, and nested classes (which NHibernate refers as Components). It also cares about Hilo and CollectionID mappings.

The second visitor checks the semantics of the mapping declared by you

Also with meaningful name: SemanticVerifierVisitor. In fact it does more than checking. It fills the information you omitted. This makes things easier for the next visitor. For example, our Blog has a “has many” relation with the Post.

    [HasMany(typeof(Post))]
    public IList Posts
    {
        get { return _posts; }
        set { _posts = value; }
    }

This visitor goes to the Post type and tries to find the reverse relation, which in this case would be a relation “belongs to Blog”. If it finds it, it uses that property and the post table to fill the information required to have a “has many” relation working with NHibernate. In the end it would be equivalent to

    [HasMany(typeof(Post), Table="PostTable", ColumnKey="blog_id")]
    public IList Posts
    {
        get { return _posts; }
        set { _posts = value; }
    }

The final visitor outputs the NHibernate xml mapping

That’s up to XmlGenerationVisitor. It also outputs the mapping xml to a file if debug is enabled. It has almost 1000 lines of code, but at the same time it’s pretty clean and easy to understand. There’s no intelligence there aside from generating correct indented Xml.

I hope this shed some more light on ActiveRecord. Stay tuned for the next article.

Posted on Sunday, April 30, 2006 9:54 PM | Back to top


Comments on this post: All you wanted to know about Castle ActiveRecord - Part II

# re: All you wanted to know about Castle ActiveRecord - Part II
Requesting Gravatar...
This is really cool stuff. Keep your excellent job. I am digging into the source code right now. Your articles are big help for me. Thank you.
Left by Harry on May 01, 2006 9:52 PM

# Castle, ActiveRecord, NHibernate...
Requesting Gravatar...
I've been busy  :)
Basically some important links to share:
First and foremost:
http://www.castleproject.org/index.php/Main_Page...
Left by NET Development Blog on Jun 18, 2006 5:50 PM

# re: All you wanted to know about Castle ActiveRecord - Part II
Requesting Gravatar...
Nobody really talks about the real-world scenarios. It's easy to understand the mapping and the blog idea in a read-only scenario, but what happens when you change the Foreign Key inside the aggregated table and want to update and save the main record, for example?

Say, for instance, we have:

Patient => Case <= Doctor

Supposing we entered a wrong Patient ID in the case table and we want to fix, what happens when you change the foreign id in the Case table and try to save the record?

Left by ebrire on May 13, 2008 11:48 AM

# re: All you wanted to know about Castle ActiveRecord - Part II
Requesting Gravatar...
Can you show me how to use select distinct ... when use active record .Thanks
Left by anphu on Oct 15, 2008 4:44 AM

# re: All you wanted to know about Castle ActiveRecord - Part II
Requesting Gravatar...
This is the scenario : Upon invoking the ActiveRecordBase<t>.Save
(entity) I want to execute the ActiveRecordBase<t>.FindAll() method.
The new inserted record could not get the details on the child class
while the other record can. This was happened everytime I inserted a
new record. I'm just wondering what is wrong.

let say here is my parent and child class.

Parent
Id
ChildID

Child
Id
Details

here is my method

void Save() {
ActiveRecordBase<t>.Save(entity);
var result = BindResult();

}

void ICollection BindResult() {
return ActiveRecordBase<t>.FindAll();

}
Left by jm on Nov 30, 2009 8:35 PM

# re: All you wanted to know about Castle ActiveRecord - Part II
Requesting Gravatar...
help i need help for my project it is in for 4 febuary HELP!!!!!
Left by Guest on Jan 29, 2010 10:07 AM

# re: All you wanted to know about Castle ActiveRecord - Part II
Requesting Gravatar...
It is Cool
Left by Firma Rehberi on Jun 04, 2010 4:05 AM

# re: All you wanted to know about Castle ActiveRecord - Part II
Requesting Gravatar...
gREat admin thanks for sharing.. dolan kartuS yeni oyunlar
Left by dantel modelleri on Oct 25, 2010 7:06 AM

# re: All you wanted to know about Castle ActiveRecord - Part II
Requesting Gravatar...
Thanks for info.. rozet bayrak
Left by plaket on Oct 25, 2010 7:07 AM

# re: All you wanted to know about Castle ActiveRecord - Part II
Requesting Gravatar...
Great thanks..
Left by plplay free kids games on Oct 25, 2010 7:09 AM

# re: All you wanted to know about Castle ActiveRecord - Part II
Requesting Gravatar...
Thanks a lot. - toner dedektif
Left by Micheal on Dec 23, 2010 6:27 AM

# re: All you wanted to know about Castle ActiveRecord - Part II
Requesting Gravatar...
thanks for the sharing
Left by bayrak on Jan 25, 2011 12:48 PM

# re: All you wanted to know about Castle ActiveRecord - Part II
Requesting Gravatar...
thank for info
but ı cant do.
are there another language copy This articles ?
Left by flag on Feb 04, 2011 1:20 PM

# re: All you wanted to know about Castle ActiveRecord - Part II
Requesting Gravatar...
This is really cool stuff. Keep your excellent job. Thank you for the sharing (:
Left by forma on Mar 28, 2011 7:04 AM

# re: All you wanted to know about Castle ActiveRecord - Part II
Requesting Gravatar...
gREat admin thanks for sharing
Left by about quran on Apr 03, 2011 11:19 PM

# re: All you wanted to know about Castle ActiveRecord - Part II
Requesting Gravatar...
help i need help for my project it is in for 4 febuary HELP!!!!!
Left by links of london jewellery on Apr 06, 2011 1:15 AM

# re: All you wanted to know about Castle ActiveRecord - Part II
Requesting Gravatar...
The computer programming is very important for our generations. Especially it is a nice information. Thanks.
bayrak
Left by flama on Feb 28, 2012 2:00 PM

Your comment:
 (will show your gravatar)


Copyright © hamilton verissimo | Powered by: GeeksWithBlogs.net