Selecting distinctly never was easier

Hi all,

Do you often select data distinctly using the NHibernate ICriteria API? If so, aren’t you sick of all the garbage you need to type in order to do that? Well, it has never been easier now that NHibernate.FlowQuery (FlowQuery) has come to your rescue.

It wasn’t a long time ago I had to write code similar to this:

var users = session.CreateCriteria<User>()
    .SetProjection
    (
        Projections.Distinct
        (
            Projections.ProjectionList()
                .Add(Projections.Property("Id"), "Id")
                .Add(Projections.Property("Username"), "Username")
                .Add(Projections.Property("Email"), "Email")
        )
    )
    .SetResultTransformer(Transformers.AliasToBean<User>())
    .List<User>();

Note that you have to specify “<User>” three times and each property name two times, ewk.

Or in this style which obviously came back and hit me in the face (performance-wise):

var users = session.CreateCriteria<User>()
    .SetProjection
    (
        Projections.ProjectionList()
            .Add(Projections.Property("Id"))
            .Add(Projections.Property("Username"))
            .Add(Projections.Property("Email"))
    )
    .SetResultTransformer(Transformers.DistinctRootEntity)
    .List<User>();

Note that you still have to specify “<User>” two times but you only need to specify the property names once, a little improvement syntax-wise, right? It is still awful performance-wise, beware!

Now, when I have FlowQuery, I only have to type this:

var users = session.FlowQuery<User>()
    .Distinct()
    .Select
    (
        x => x.Id, 
        x => x.Username, 
        x => x.Email
    );

Simply says: Construct the users for me but only populate these properties.

Or this:

var users = session.FlowQuery<User>()
    .Distinct()
    .Select(x => new User(x.Username)
    {
        Id = x.Id,
        Email = x.Email
    });

Says: Fetch this data and use this constructor and member initializer.

Or to an anonymous type, this:

var users = session.FlowQuery<User>()
    .Distinct()
    .Select(x => new
    {
        x.Id,
        x.Username,
        x.Email
    });

Says something like: Hey, give me these properties, type? I dunno, just give it to me.

Or if I want or need to, this:

var users = session.FlowQuery<User>()
    .Distinct()
    .Select<User>()
        .For(x => x.Id).Use(x => x.Id)
        .For(x => x.Username).Use(x => x.Username)
        .For(x => x.Email).Use(x => x.Email)
        .Select();

Very handy if some of the properties of the destination type have hidden setters. That extra “<User>” is not necessary in the next version.

What I want to demonstrate here is that with FlowQuery you have a couple of more choices of which all are type-safe, refactoring-friendly and of which all builds the better version of the ICriteria Queries I presented earlier. The best thing yet is that it is as easy as taking a dump in the morning or making a sandwich.

Enjoy! (:

/Niklas Källander

Advertisements