This is the third post of a series started with this post about a MonoRail and ActiveRecord tutorial.
There are several approaches for writing the domain model classes for a MonoRail and ActiveRecord application:
- Create the domain model classes from a generator, providing a database schema
- Create the domain model classes from scratch
Consequently, if you have chosen to create the domain model classes from scratch, after you have created the domain model classes you can auto-generate the database schema.
If instead you have already the database schema, you could opt for the first point (and maybe retouch them after the generation).
For the aims of this tutorial, we will first write the domain model classes and then auto-generate the database schema.
These are the domain model classes: copy and paste them under the model folder (once again this is a convention, you can add them in a different place):
This is the Album domain class:
using System; using System.Collections; using Castle.ActiveRecord; using NHibernate.Expression; namespace net.paolocorti.Samples.Castle.Tutorial.Models { /// <summary> /// Album class /// </summary> [ActiveRecord] public class Album : ActiveRecordValidationBase { private int id; private string name; private Int16 year; private Support support; private IList artists = new ArrayList(); private IList genres = new ArrayList(); /// <summary> /// Primary Key /// </summary> [PrimaryKey(PrimaryKeyType.Native)] public int Id { get { return id; } set { id = value; } } /// <summary> /// Name /// </summary> [Property(NotNull = true, Length = 150), ValidateNotEmpty("You need to provide the name of the album!"), ValidateIsUnique("There is already this album!")] public string Name { get { return name; } set { name = value; } } /// <summary> /// Year /// </summary> [Property, ValidateRegExp(@"\d{4}", "You need to provide a valid year!")] //ValidateRegExp(Year) public Int16 Year { get { return year; } set { year = value; } } /// <summary> /// Support /// </summary> [BelongsTo("supportid")] public Support Support { get { return support; } set { support = value; } } /// <summary> /// Artists /// </summary> [HasAndBelongsToMany(typeof(Artist), Table = "ArtistAlbum", ColumnKey = "albumid", ColumnRef = "artistid")] public IList Artists { get { return artists; } set { artists = value; } } /// <summary> /// Genres /// </summary> [HasAndBelongsToMany(typeof(Genre), Table = "AlbumGenre", ColumnKey = "albumid", ColumnRef = "genreid")] public IList Genres { get { return genres; } set { genres = value; } } /// <summary> /// Get an HTML view of genre names /// </summary> public string HtmlGenresNames { get { string genresnames = ""; foreach (Genre genre in Genres) { genresnames += genre.Name; AlbumGenre ag = AlbumGenre.Find(this, genre); if (ag.Main && Genres.Count>1) { genresnames += "(*)"; } genresnames += "<br/>"; } return genresnames; } } /// <summary> /// Get an HTML view of artist names /// </summary> public string HtmlArtistsNames { get { string ArtistsNames = ""; foreach (Artist artist in Artists) { ArtistsNames += @"<a href=""/artist/edit.rails?id=" + artist.Id + @""" />" + artist.Name + "<br/>"; } return ArtistsNames; } } /// <summary> /// Find all albums /// </summary> /// <returns></returns> public static Album[] FindAll() { return (Album[])FindAll(typeof(Album), new Order[] { Order.Asc("Name") }); } /// <summary> /// Find album by Id /// </summary> /// <param name="id">Id of Album</param> /// <returns></returns> public static Album FindById(int id) { return (Album)FindByPrimaryKey(typeof(Album), id); } } }
Let’s inspect more deeply the Album domain class.
We can notice that it is composed by 6 properties that are mapping the database Album entity (Id, Name, Year, Support, Artists and Genres), two properties to get some HTML from the view (HtmlGenresNames and HtmlArtistsNames), and some method to perform search (FindAll, FindById).
Note for the purist: I mixed here HTML (in the properties HtmlGenresNames and HtmlArtistsNames, to be consumed by the views) in the Domain Model to keep things simpler, but you can easily find a better way to manage this.
A domain model class with ActiveRecord must inherit ActiveRecordBase
The class is derived from the ActiveRecordBase class (in this case ActiveRecordValidationBase, that is inherited from ActiveRecordBase, as far we want to provide a simple validation for the user interface experience). By inheriting from these classes, you provide the Active Record design pattern implementation at your domain class. By these, in fact, you will be able to use the Active Record methods (eventually overloaded) for the CRUD functionalities (look at the following picture).
So, by inheriting ActiveRecordBase, our Album classes will have the following CRUD methods for free: Create, Delete, Update. Other usefull methods (in various ways) are CountAll, DeleteAll, EnumerateQuery, Exists, FindAll, FindAllByProperty, FindByPrimaryKey, FindFirst, FindOne.
By inheriting the ActiveRecordValidationBase we will have other useful methods, including those from ActiveRecordBase: IsValid and NotValid. Plus, we get two properties for managing data validation: PropertiesValidationErrorMessage and ValidationErrorMessages (we will use this when writing the controllers).
Class attributes
As you can see from the following image, when you declare the class you can set some attributes.
Read carefully the NHibernate documentation, Class element, if and how to set these attributes.
Important: I have noticed that the NHibernate documentation is not aligned with the actual release. So feel free, for better understanding, to integrate it with the Hibernate doc.
Here I explicitly refer to some of the most important, but please read the doc for more complete information. And remember that you should, like in every agile framework, follow the convention over configuration: for example name the class with the same name of the table in the RDBMS and you will not need to explicitly set the table attribute in your class declaration.
- name: there is not need to set it
- table: name of the database table (set it if it is different from your class)
- discriminator-value: if you wish to implement table polymorphism (subclasses)
- where: an SQL where condition for retrieving a subset of rows and not the whole set
- optimistic-lock: for the optimistic locking strategy
- lazy: set to true if you want to implement for your class the lazy load design pattern
For this tutorial we will not set any attributes, this meaning that database table names will be the same of the domain model classes, that we will not implement table polymorphism (we don’t have this situation in our ER diagram), that we will not use the lazy load.
So when we will auto-generate the database schema there will be a table named Album.
A domain model class with ActiveRecord must have an unique identifier (Primary Key)
Every ActiveRecord model class must have an identifier. This is provided by the PrimaryKey attribute that must be placed at the property which uniquely identify our rows (entities). In the Album class the identifier is the Id property.
The PrimaryKey attribute requires a PrimaryKeyType generator and some named parameters.
For choosing the PrimaryKeyType generator is important to read and understand the NHibernate documentation about the generator element. You will find out that NHibernate (who is behind the scenes in CastleProject ActiveRecord) provide a range of built in implementations. Read carefully this documentation to have a better idea on how to set the PrimaryKeyType generator for your primary key. Anyway I have noticed that setting Native (which will choose the right implementation for your RDBMS: identity, sequence or hilo) does not perform well with Postgres. For Postgres I suggest to explicitily set it to sequence (also provide a sequence name if you wish, otherwise NHibernate will create an own sequence that will be used for every table in your domain model).
For me setting PrimaryKeyType to native performs well with MS SQL, MySQL but I have explicitily to set it to Sequence for Postgres. If you want to explicitily set which PrimaryKeyType to use for your PrimaryKey, you should use identity for DB2, MySQL, SQL Server and Sybase, and sequence for Postgres, DB2, Oracle and Firebird.
If you are in doubt, study the NHibernate generator element documentation and the possible values for the class attribute (increment, identity, sequence, hilo, seqhilo, uuid.hex, uuid.string, guid, guid.comb, native, assigned, foreign).
You can then explicitly set some named parameter or use the defaults (look the picture):
Here the NHibernate (Hibernate) doc is still your best friend. You have to look at the Id element and its attributes. The most important are:
- name: there is not need to set it
- type (ColumnType): the datatype for the column
- column: name of the column
Again here we will use convention over configuration and we will not set any of these attribute. This meaning that the column name in the table of the database for our id field will be the same as the name we are giving for the identifier in the ActiveRecord class, and that the datatype for this column in the database will be translated from the .NET datatype we are using for the identifier.
So when we will auto-generate the database schema there will be an Id column at the Album table that will be the primary key. The datatype will be an Int32 equivalent to your RDBMS implementation (if you are using Postgres will be an integer, for example).
Mapping database column in the domain class
For mapping our class properties (Name, Year, Support, Artists, Genres) in the database we will use the Property attribute and the relationship’s attributes (BelongsTo and HasAndBelongsToMany).
For the Property element the most important attributes are (still look at Nhibernate-Hibernate doc):
- name: there is not need to set it
- column: name of the column
- type (ColumnType): the datatype for the column
- formula: use if you have computed columns
- not-null: if you want to set in the database a nullable constraint for the column
- index: name of the index
- length: length of the column
Using convention over configuration, the Name property of the Album class will reflect in a column named Name in the Album table. Its type, as is not given, will be reflect the .NET type of the property (string), so will be translated in the string implementation of your RDBMS (for example in Postgres will be character). As far we have explicitly set the NotNull and Lenght attributes, a nullable constraint for this column will be created, and its length will be 150. For example in Postgres, when we will auto-generate the schema, this will be translated as:
name character varying(150) NOT NULL
Validation
We have inherited from ActiveRecordValidationBase, so we have some attribute to use in properties out of the box to provide validation (look at the image).
It is very easy to use this out of the box feature, look for example how to provide basic validation (NotEmpty and IsUnique) for the Name property:
/// <summary> /// Name /// </summary> [Property(NotNull = true, Length = 150), ValidateNotEmpty("You need to provide the name of the album!"), ValidateIsUnique("There is already this album!")] public string Name { get { return name; } set { name = value; } }
You can also validate against a Regular Expression, as showed in the Year property. For more information about providing validation in the domain classes look here.
Next time I will show you how to work with ActiveRecord relationships, for mapping the Support, Artists and Genres properties of the Album class.















