NHibernate Tooling Review: LLBLGen Pro 3.0

time to read 9 min | 1658 words

This is part of what looks to be long series of posts about tooling relating to NHibernate.

In this case, I want to talk about LLBLGen Pro.

LLBLGen Pro 3.0 is an OR/M tool that also comes with an NHibernate option. Because it is the GUI tool to manage NHibernate mapping that I am testing, I am going to show a lot of screenshots. Also, please note that I am using this without any foreknowledge, reading the docs, or anything. I am also not going to do an in depth discussion, just use it to see how those things are working.

After installation, we start a new project:

image

Which give us the following (scary) dialog. Note all the tabs in the bottom. There is a huge number of options and knows that you can tweak.

image

The next step that I wanted to do is to see how it handles an existing data model, so I decided to add the test model that I use for NHibernate:

image image

This takes about zero time and gives us the entities. It properly recognize 1:m associations, but I had to define the m:n associations myself. I am not sure if this is what I am supposed to do or if I just missed something. What is impressive is that the schema in question is actually being generated from NHibernate through the SchemaExport tool. So we went a full cycle, NHibernate mapping, schema, extracting everything to the following model:

model

I wonder if I can get LLBLGen Pro to read a set of NHibernate mapping as well, because this view of the mapping can be very useful.

But enough talking about the UI, there are a lot of tools that have great UI, I want to see what code it generates.

As an aside, I like this part of the code generation:

image

Not so much because what there is there, but because it implies how the code generation process itself is structured.

I was surprised when I saw this:

image image

It brought back flashbacks of 1.0, but they are empty, I assume that this is vestigial remain in the template.

I don’t like that CategoriesPost is there, but I couldn’t figure out how to remove it, this isn’t an entity, it is an association table.

Looking at the code, there is one thing that really bothers me, take a look at the property definitions. How do you set that?

image

I am pretty sure that I did something to cause that, but I really don’t have any idea what.

Update: (This is written before the post is published, but after I had time to review what I did). It appears that I played with too many settings, there is an option called EmitFieldSetter that I accidently set to false. The idea behind this setting that you want to allow setting properties via methods, not via direct properties.

I’ll ignore that for now, and look at the rest of the code. In addition to the Model project, LLBLGen Pro also generates the Persistence project:

image

I was quite surprised to see that it uses class maps, but then I looked at the code:

/// <summary>Represents the mapping of the 'Blog' entity, represented by the 'Blog' class.</summary>
public class BlogMap : ClassMap<Blog>
{
    /// <summary>Initializes a new instance of the <see cref="BlogMap"/> class.</summary>
    public BlogMap()
    {
        Table("[dbo].[Blogs]");
        OptimisticLock.Version();
        LazyLoad();

        Id(x=>x.Id)
            .Access.CamelCaseField(Prefix.Underscore)
            .Column("Id")
            .GeneratedBy.Identity();
        Map(x=>x.AllowsComments).Access.CamelCaseField(Prefix.Underscore);
        Map(x=>x.CreatedAt).Access.CamelCaseField(Prefix.Underscore);
        Map(x=>x.Subtitle).Access.CamelCaseField(Prefix.Underscore);
        Map(x=>x.Title).Access.CamelCaseField(Prefix.Underscore);

        HasMany(x=>x.Posts)
            .Access.CamelCaseField(Prefix.Underscore)
            .Cascade.AllDeleteOrphan()
            .Fetch.Select()
            .AsSet()
            .Inverse()
            .LazyLoad()
            .KeyColumns.Add("BlogId");
        HasManyToMany(x=>x.Users)
            .Access.CamelCaseField(Prefix.Underscore)
            .Cascade.AllDeleteOrphan()
            .Fetch.Select()
            .AsSet()
            .Table("[dbo].[UsersBlogs]")
            .ParentKeyColumns.Add("BlogId")
            .ChildKeyColumns.Add("UserId");

    } 
} 

That is highly readable, and a lot of people prefer that, and I guess it is easier than generating the XML.

Did you see the SessionManager class? It also handles all the wiring of NHibernate, so you can start using it by just getting the session:

SessionManager.OpenSession()

Overall, very impressive. From installation to working code (except that annoying no setter thingie), it took mere minutes, and the code is clean. That is one of the things that I always hated with code generators, they generally produce crappy code, so I was very happy to see how the code looked like.

Now, after the whirlwind tour, let me see if I can look at more of the options.

Oh, it looks like it can generate HBM files, let us give that a try:

<class name="Blog" table="[dbo].[Blogs]" optimistic-lock="version" >
    <id name="Id" column="Id" access="field.camelcase-underscore" >
        <generator class="identity"/>
    </id>
    <property name="AllowsComments" column="AllowsComments" access="field.camelcase-underscore"/>
    <property name="CreatedAt" column="CreatedAt" access="field.camelcase-underscore"/>
    <property name="Subtitle" column="Subtitle" access="field.camelcase-underscore"/>
    <property name="Title" column="Title" access="field.camelcase-underscore"/>
    <set name="Posts" access="field.camelcase-underscore" cascade="all-delete-orphan" inverse="true" fetch="select">
        <key>
            <column name="BlogId"/>
        </key>
        <one-to-many class="Post"/>
    </set>
    <set name="Users" access="field.camelcase-underscore" table="[dbo].[UsersBlogs]" cascade="all-delete-orphan" fetch="select">
        <key>
            <column name="BlogId"/>
        </key>
        <many-to-many class="User">
            <column name="UserId"/>
        </many-to-many>
    </set>
</class>

There is one main thing to note here, this looks pretty much like it was written by a human. There are still some things to improve, but compare that to the output of Fluent NHibernate, and you’ll see that this is generating something that is much easier to work with.

Some things that still bothers me:

  • Kick all the access=”field.camelcase-underscore” to the default-access on the <hibernate-mapping> element, that would remove a lot of duplication.
  • The table=”[dbo].[UsersBlogs]” should be table=”`UsersBlogs`” schema=”`dbo`”, and I would skip the catalog if it is dbo already.
  • The class contains an optimistic-lock=”version”, but there is no version column, so this should go away as well. The same error appears in the fluent nhibernate mapping as well.

Let us see what else I can play with.

There is something called Typed Views, which I didn’t understand at first:

image

It appears that instead of generating an entity (which has associations, collections, references, etc), this build just a bare bone class to hold the data. The purpose, I believe, is to use this for reporting/binding scenarios, in which case a view model is much more appropriate. This is one features that the CQRS guys are probably going to like.

Oh, I found the m:n option:

image

With that, I get:

image

I probably need to change the name generation pattern, though :-)

I would suggest running the labels on the UI through PascalCaseToSentence, though. That is, instead of having to read AutoAddManyToManyRelationships, turn that to “Auto add many to many relationships”.

There is another feature, called TypedList, which gives you a query designer for building NHibernate queries (they are translated to HQL) which looked nice, but I haven’t looked at too closely. The way I usually work, queries are too context sensitive to allow to create the sort of query library that would make this feature useful.

This has been a very short run through LLBLGen Pro in its NHibernate mode. Overall, it looks very impressive, and it generates the best code that I have seen yet out of a tool. Good enough to take and use as is.

The only detraction that I could find is the name, (yes, I know that I talked about this before), LLBLGen Pro just doesn’t roll off the tongue.