NHibernate 3 Linq and IUserType

352 views Asked by At

I have a table:

Page (
   Id int,
   Name nvarchar(50),
   TemplateName varchar(50)
   ...
)

and it's mapped to domain model:

public class Page {
   public virtual int Id { get; set; }
   public virtual string Name { get; set; }
   public virtual Template Template { get; set; }
}

Note that in the domain model, "Template" property is not of type "string". The Template class is like this:

public class Template {
   public string Name { get; set; }
   // other properties...
}

"Templates" are loaded from file system. I have a TemplateManager class:

public class TemplateManager {
    public static Template LoadTemplate(string templateName) {
        // check if there's a folder named <templateName>
    }
}

I can use IUserType to map the "Template" property.

public class PageMap : ClassMapping<Page> {
    public PageMap() {
        ...
        Property(c => c.Template, m => {
             m.Column("TemplateName");
             m.Type<TemplateUserType>();
        }
    }
}

public class TemplateUserType : IUserType {
    public object NullSafeGet(System.Data.IDataReader rs, string[] names, object owner)
    {
        var templateName = rs[names[0]].AsString();

        if (!String.IsNullOrEmpty(templateName))
        {
            return TemplateManager.LoadTemplate(templateName);
        }

        return null;
    }
}

Okay, so far so good. But the problem is, how can I use Template property in Linq queries? For exmaple:

var pages = session.Query<Page>().Where(it => it.Template.Name == "MyTemplate");

I think the solution might be to write a class (say TemplatePropertyHqlGenerator) implementing IHqlGeneratorForProperty. This is the linq query extension point provided by NHibernate 3. But how to write this TemplatePropertyHqlGenerator class?

Thanks in advanced!

1

There are 1 answers

0
Paul Turner On

The IUserType interface lets you define a type which is considered atomic. That is to say, you can then perform direct comparisons between instances of the type and NHibernate will know how to translate them.

E.g. the following query would evaluate:

var template = new Template();
session.Query<Page>().Where(it => it.Template == template);

If you want to define a type which has component values which you can then manipulate, you need to implement the ICompositeUserType interface. This interface requires you define the properties of the type as atomic elements, giving NHibernate the information it needs to understand the specific properties of the type.

As a result, it's a little more complex than IUserType to implement, but it should facilitate what you want to achieve.

Here's an understandable example of implementing the interface for a Money type: http://geekswithblogs.net/opiesblog/archive/2006/08/05/87218.aspx