Login

Username

Password





Register   Reset password

Developer resources

Developer documents

RSS-2.0

Developer articles (Archive)

  • NHibernate and Cuyahoga part 2: mappings

    When using a powerful O/R mapper like NHibernate, mapping classes to the database can sometimes be tricky. This causes a lot of frustration, especially because there are not so many real-world mapping examples to find (that is, for NHibernate, for the Java version there is much more documentation).

    Note: for a better understanding of this article it’s advisable to have the Cuyahoga source code by hand. There will be some references to the source code in the article.

    Mapping files

    The mapping files are organized like in most other NHibernate examples. There is a single .hbm.xml file for each domain class that contains the mappings. The files are compiled as embedded resource into the assemblies. You can find the mapping files together with the domain classes in the Cuyahoga/Core/Domain folder.

    In the modules, the mapping files are in the same folder as the other module files (the Articles module is the best example here).

    Common aspects of the Cuyahoga mapping files

    Most mapping files have two things in common:
    1. Primary key values are auto-generated by the database. This results in the following element in the mapping file:
      <id name="Id" column="nodeid" type="Int32" unsaved-value="-1">
          <generator class="native">
              <param name="sequence">cuyahoga_node_nodeid_seq</param>
          </generator>
      </id>
      The sequence element is needed for PostgreSQL and with other databases (SQL Server, MySQL) it is just ignored.
    2. Timestamps are used for optimistic concurrency:
      <timestamp name="UpdateTimestamp" column="updatetimestamp" />
      It is advisable to use version numbers for this (<version> element), but since Cuyahoga already had updatetimestamp fields in the database before it even used NHibernate, it was easier to do it this way.

    So, how can I learn a bit more about mappings?

    Well, it’s really too much to explain everything in detail. The best way is to explore the code with a little aid of the Class Diagram and the Database Diagram.

    The Cuyahoga.Core.Domain.Node class is perhaps the most interesting to start with. It has several relationships to other classes, including bidirectional ones (yes, the inverse thing) and there is even a sample of how to map an association class (NodePermission).

    For the full picture, you also might want to check the code-behind of the Admin pages (Cuyahoga/Web/Admin) to see how you can work with the mapped objects from .aspx pages. These admin pages are more straightforward and easier to understand than, for example, the Cuyahoga.Web.UI..PageEngine class that builds the pages for the end-users.

    Reference

    The table below gives an indication of the mappings that are in Cuyahoga and where to find these.

    Relation type

    Collection type

    Mapping file

    Classes

    Remarks

    one-many

    bag

    Node.hbm.xml

    Node <-> Section

    bi-directional

     

     

     

    Node <-> Node (parent-child)

    bi-directional

     

     

    ModuleType.hbm.xml

    ModuleType -> ModuleSetting

    composite element

     

    map

    Section.hbm.xml

    Section -> Settings

    name-value pairs

     

     

     

     

     

     

     

     

     

     

    many-many

    bag

    User.hbm.xml

    User -> Role

     

     

     

    Node.hbm.xml

    Node -> Role

    NodePermission as association class, composite element

     

    list

    Menu.hbm.xml

    Menu -> Node

     

     

     

     

     

     

    many-one

    N/A

    Node.hbm.xml

    Node <- Site

    bi-directional

     

     

     

    Node <- Template

     

     

     

     

    Node <- Node (parent-child)

    bi-directional

     

     

    Section.hbm.xml

    Section <- ModuleType

     

     

     

     

    Section <- Node

    bi-directional

     

     

    Article.hbm.xml (in ArticleModule)

    Article <- Category

     

     

     

     

    Article <- Section

    multi-assembly

     

     

     

    Article <- User

    multi-assembly

    5/11/2005 12:27:00 PM Published by Martijn Boland Category Developers Comments 6
  • NHibernate and Cuyahoga part 1: configuration and session management.

    This is a first article in a series where I’m going to try and explain how the Cuyahoga web site framework uses NHibernate for object persistence. The versions being used in this article series are 0.7.0.1 for Cuyahoga and 0.7 for NHibernate.

    Configuration

    So let’s start with the NHibernate configuration. This is the place where mappings between objects and the database live at run time. In Cuyahoga, this configuration is encapsulated with the Cuyahoga.Core.Service.SessionFactory class. This class serves as a wrapper for the NHibernate Configuration and SessionFactory classes. It is designed as a singleton because building the configuration is expensive and all application threads can safely use the same instance. At creation time the configuration for the Cuyahoga core classes is built and an ISessionFactory instance is created:

    Configuration config = new Configuration();
    this._nhibernateConfiguration = config.AddAssembly(this.GetType().Assembly);
    this._nhibernateFactory = this._nhibernateConfiguration.BuildSessionFactory();

    Since the core domain classes are in the same assembly as the SessionFactory, we can add them all at once to the configuration by simply adding the assembly (this.GetType().Assembly). Note that the Cuyahoga SessionFactory keeps a reference to the NHibernate configuration. The docs say that this one is to be discarded, but we need it for adding the mappings of the separate Cuyahoga modules.
    Most modules have their own mappings but they are not loaded until they are first requested, so it’s impossible to build the configuration for the core classes and the modules all at once at creation time. The RegisterPersistentClass() method makes it possible for modules to still join the mapping party: 

    /// <summary>
    /// Add a class to the NHibernate mappings.
    /// If the class already is mapped, nothing will happen.
    /// </summary>
    /// <param name="type"></param>
    public void RegisterPersistentClass(Type type)
    {
    if (this._nhibernateConfiguration.GetClassMapping(type) == null)
    {
    // Class isn't mapped yet, so do it now.
    this._nhibernateConfiguration.AddClass(type);
    this._classesAdded = true;
    }
    }

    Note that the NHibernate SessionFactory isn’t recreated immediately. Modules may add multiple classes to the configuration in one go but they also have the responsibility to call the Rebuild() method when done.

    Session management

    After configuration we have everything in place for the real action. With the GetSession() method, an open NHibernate session can be obtained to do the persistence of objects.

    Cuyahoga doesn’t use ‘raw’ NHibernate sessions though for persisting the core objects, but has a façade class Cuyahoga.Core.Service.CoreRepository that hides almost all NHibernate specific things for the rest of Cuyahoga. This class contains generic methods for retrieving, saving, updating and deleting objects of a specific type and some methods for more specific operations (like filtering and sorting). Internally, CoreRepository uses a NHibernate session that is obtained from the SessionFactory.

     The session management follows the pattern that is called ‘Session per request’. At the beginning of each request, a single NHibernate session is created that is used during the entire ASP.NET page lifecycle. Normally this is done by storing the session in the HttpContext.Current.Items collection. Cuyahoga does it a little different by storing an instance of the CoreRepository class, but conceptually this is more or less the same.

    There is a dedicated HttpModule that deals with storing the CoreRepository to the request: Cuyahoga.Web.Util.NHSessionModule. There is a BeginRequest event handler that creates the CoreRepository instance and an EndRequest event handler that closes the session:

    private void Context_BeginRequest(object sender, EventArgs e)
    {
    // Create the repository for Core objects and add it to the current HttpContext.
    CoreRepository cr = new CoreRepository(true);
    HttpContext.Current.Items.Add("CoreRepository", cr);
    }
    private void Context_EndRequest(object sender, EventArgs e)
    {
    // Close the NHibernate session.
    if (HttpContext.Current.Items["CoreRepository"] != null)
    {
    CoreRepository cr = (CoreRepository)HttpContext.Current.Items["CoreRepository"];
    cr.CloseSession();
    }
    }
     You could also put this in the Global.asax, but that’s a matter of personal preferences. I just happen to like HttpModules.
    Now, the ASP.NET pages and user controls in Cuyahoga can access the CoreRepository at any time during the page lifecycle.

    The CoreRepository doesn’t have to offer much functionality for the modules, so they have to manage the persistence themselves. Examples of this can be found in the classes that inherit from Cuyahoga.Core.Domain.ModuleBase like Cuyahoga.Modules.Articles.ArticleModule or Cuyahoga.Modules.StaticHtml.StaticHtmlModule.

    It’s important that the modules use the same session as the Cuyahoga.Web.UI.PageEngine class that builds the pages. They can obtain that session from the CoreRepository that is in the Context.Items collection. Currently however, they raise an event when they need a session (OnNHSessionRequired) that is handled by the PageEngine. This is a little heritance from earlier days when Cuyahoga didn’t use the Context.Items to store the session. To be refactored…;).

    4/26/2005 12:36:00 PM Published by Martijn Boland Category Developers Comments 6
  • Explained: Indexing and searching content

    Cuyahoga uses DotLucene to index the content that has to be searched. DotLucene is a .NET port of the famous Jakarta Lucene engine. It is not a complete search engine but more like a very powerful API that can be integrated in almost every application that needs full text search capabilities.
    All content in DotLucene is a Document. To index content, you have to feed Document instances to DotLucene and after a query, it will (indirectly, via Hits) return Document.

    Modules in Cuyahoga that want to have their content indexed need to implement the ISearchable interface. This interface consists of one method (GetAllSearchableContent) that retrieves all documents for that module and three events (ContentCreated, ContentUpdated, ContentDeleted), that have to be fired by the module after changing content. These events contain the document  that has to be indexed or removed from the index.
    Implementing was pretty easy because all module adminstration pages inherit from the same base class. This base class handles the events and calls the appropriate methods of the index builder.
    The advantage of the event-based indexing is that all content is indexed immediately. There is no need for a scheduler that crawles the site to update the search index. If anything goes wrong you can always manually build a new search index.

    When rebuilding the entire search index from scratch, Cuyahoga traverses the sites, nodes and sections and when it finds a module connected to a section that implements ISearchable, it calls GetAllSearchableContent() to retrieve the documents that have to be indexed.
    2/19/2005 5:31:00 PM Published by Martijn Boland Category Developers Comments 4
  • Issues solved with running Cuyahoga on Mono

    In a previous post I mentioned some problems that prevented Cuyahoga from running on Mono.
    The issue with DynamicProxy still exists but I've created alternative NHibernate mapping files for Mono without proxies. When building with NAnt  on Mono, the original mapping files are replaced with the Mono versions.
    One other main step was to replace the customized FreeTextBox editor with FCKEditor because FreeTextBox stopped working on Mono 1.1.3. FCKEditor is a pretty good editor and it's completely open source.
    The issue with the pathinfo variables was fixed by using an overload of HttpContext.RewritePath that accepts path, pathinfo and querystring as separate parameters. This overload works as expected.
    1/16/2005 2:28:00 AM Published by Martijn Boland Category Developers Comments 1
  • Some goodies that might be overlooked :)

    Did you perhaps notice the Cuyahoga.ServerControls assembly? Currently it contains three controls:
    Calendar
    This is an ASP.NET server control that wraps mishoo's terrific javascript calendar. Simply drag it on your web form, set the path to the directory where the support files are located, and you've got a nice control for entering dates that will let you immediately forget the dreadful built-in ASP.NET calendar :).

    Pager
    With ASP.NET, only the datagrid has paging facilities. What if, for example, you want to use a repeater (because you like clean HTML) and also want paging? Cuyahoga has a pager control that can be attached to any web control that can have a datasource. Drag it to your web form, select the control that you want to be paged and you're done. For the more demanding users, there is a possibility to cache results.

    CuyahogaEditor
    This is a wrapper around FreeTextBox 2.x. Nothing special here, only an image gallery and a link browser designed for Cuyahoga. I will probably replace it with a version of FreeTextBox 3.0 that has a built-in image gallery.

    [Edit 2005-01-16]
    CuyahogaEditor is removed from Cuyahoga.ServerControls because it is replaced with a plain vanilla FCKEditor.
    1/5/2005 11:20:00 PM Published by Martijn Boland Category Developers Comments 0
  • Dynamic modules and O/R mapping with NHibernate

    One of the more challenging things of O/R mapping is how to handle modules that are dynamically loaded (especially when they are in different assemblies). The core engine of the website doesn't know which modules are going to be loaded so it can only initialize the mapping of it's own domain classes.

    Cuyahoga handles this by providing a central facility (a Singleton wrapper around NHibernate's SessionFactory) where modules can register their domain classes. When registering, a check is being performed if the class is already mapped. If not, the classes are added to the mapping configuration and the SessionFactory is rebuilt. The caller recieves a notification that the SessionFactory is rebuilt and stops executing. The browser is then redirected to the same url whereafter page execution starts again, but now with the mapping information in place and nicely integrated with the mapping information of the core domain classes.
    1/5/2005 10:42:00 PM Published by Martijn Boland Category Developers Comments 1
Current