NHibernate.FlowQuery v0.8 Released

Today I finally got the time to release a new version of NHibernate.FlowQuery ( FlowQuery ). Version 0.8 of FlowQuery can be downloaded here. The new version contains one new feature ( OrderBy<TProjection>, which lets you order your result set by one ( or more ) of the projections ), a few bug fixes ( Is.Not.* in sub-queries now actually negates the expression which it, somehow, didn’t in the previous version ) and a minor change in the API ( an IRevealer now exposes its’ IRevealConvention ).

The new feature can be used like this:

var users = session.FlowQuery<User>()
    .OrderBy<UserDto>(x => x.Name)
    .Select(x => new UserDto()
    {
        Name = x.Firstname + " " + x.Lastname
    });

This is actually the same as doing:

var users = session.FlowQuery<User>()
    .OrderBy(x => x.Firstname + " " + x.Lastname)
    .Select(x => new UserDto()
    {
        Name = x.Firstname + " " + x.Lastname
    });

But it’s a bit more convenient, don’t you think?

The new feature also provides overloads to support strings:

var users = session.FlowQuery<User>()
    .OrderBy<UserDto>("Name")
    .Select(x => new UserDto()
    {
        Name = x.Firstname + " " + x.Lastname
    });

Every overload also has a matching OrderByDescending method and the new feature can also be combined with the old OrderBy-syntax with ease:

var users = session.FlowQuery<User>()
    .OrderBy(x => x.Age)
    .OrderBy<UserDto>(x => x.Name)
    .Select(x => new UserDto()
    {
        Age = x.Age,
        Name = x.Firstname + " " + x.Lastname
    });

The only possible downside is that it can only be used with projections made using ISelectSetup ( .For(x => x.Prop).Use(x => x.Prop) ) or a lambda expression ( member initializer ). It does not work using IProjections.

Enjoy! (:

/Niklas Källander

NHibernate.FlowQuery v0.7 Released

Today I released a minor update of NHibernate.FlowQuery (FlowQuery). Version 0.7 of FlowQuery can be downloaded here. The new version merely adds a small amount of functionality for joining hidden members and using select setup when projecting queries. It also fixes a bug when selecting hidden fields (which was somehow overlooked in the previous version).

The added functionality for joining hidden members is only a few overloads on the Join-methods to pipeline lambda expressions on to the Reveal-helper. This to reduce verbosity when joining.

An example, with the previous version:

Post post = null;
Comment comment = null;

session.FlowQuery<User>()
    .Join(u => u.Posts, () => post)
    .Join(Reveal.ByConvention(() => post.Comments), () => comment)
    .Select();

With the new version:

Post post = null;
Comment comment = null;

session.FlowQuery<User>()
    .Join(u => u.Posts, () => post)
    .Join(() => post.Comments, () => comment)
    .Select();

For the sake of convenience there is also overloads to provide your own IRevealConvention:

session.FlowQuery<User>()
    .Join(u => u.Posts, () => post)
    .Join(() => post.Comments, () => comment, new UnderscoreConvention())
    .Select();

In the previous version of FlowQuery you could not use strings in the Use-part of a select setup. In the new version this is fixed and you don’t have to create your own PropertyProjection to overcome this misfortune. In other words…

session.FlowQuery<User>()
    .SelectDistinct()
        .For(x => x.Firstname).Use("Firstname")
        .Select();

…is now possible!

Enjoy! (:

/Niklas Källander

Aggregation with NHibernate.FlowQuery

Aggregation is quite easy in NHibernate.FlowQuery (FlowQuery) but in can be a bit unclear how to do it. This post is supposed to make it a bit clearer by giving a couple of examples.

With FlowQuery all aggregation is done by using the Aggregate-helper, like:

session.FlowQuery<User>()
    .Select(x => new
    {
        Average = Aggregate.Average(x.Id),
        Max = Aggregate.Max(x.Id),
        Min = Aggregate.Min(x.Id)
    });

This will return a FlowQuerySelection<Anonymous> containing one item that holds the selected aggregations.

Quite easy, right?

To group by a certain property, you also have to select that certain property in a GroupBy-aggregation, like:

session.FlowQuery<User>()
    .Select(x => new
    {
        Average = Aggregate.Average(x.Id),
        Max = Aggregate.Max(x.Id),
        Min = Aggregate.Min(x.Id),
        GroupedBy = Aggregate.GroupBy(x.Firstname)
    });

Now the aggregation will be grouped by Firstname and the returned FlowQuerySelection<Anonymous> will no longer contain only one item but instead one item for each unique Firstname in the database table.

Enjoy! (:

/Niklas Källander

NHibernate.FlowQuery v0.6 Released

Today I released a new version of NHibernate.FlowQuery (FlowQuery). Version 0.6 of FlowQuery can be downloaded here. The new version contains a couple of new and improved features and fixes a few bugs.

New and improved features:

  • Possibility to build a fluent query which uses the Is-helper, which further gives you the ability to combine several Is-helper-calls with both Or– and And-statements.
  • “Advanced” auto-mapping functionality. You can now provide your own mapper which in turn could be based on AutoMapper or any other mapping tool.
  • Improved and extended subqueries support. It is now possible to refer to the main query in the subquery and it is also possible to perform a few more restrictions using subqueries.

The new version also have 100% code coverage by unit testing.

Other changes:

  • FlowQueryOptions is reduced to having only a single public method called Add which takes an Action<ICriteria>.
  • The auto-mapping functionality is no longer performed in the select-statements, it is moved to FlowQuerySelection<TSource> which is returned by the select-statements.
  • The Not-member of the Is-helper is no longer a method taking an IsExpression, it is merely a property which negates the following IsExpression.

Examples:

Building a fluent query with the Is-helper:

var users = session.FlowQuery<User>()
    .Where((u, where) => u.Username == "Niklas"
                        && (where(u.Role, Is.Not.EqualTo(Role.Administrator))
                        || u.Lastname.Contains("K")))
    .Select();

where in this case is a WhereDelegate which takes an object and an IsExpression and returns a bool.

Referencing the main query from a subquery:

User user = null;

var subquery = SubQuery.For<User>()
    .SetRootAlias<User>(() => user)
    .Where(x => x.Id == user.Id)
    .Select(x => x.Id);

var users = session.FlowQuery<User>(() => user)
    .Where(u => u.Id, Is.In(subquery))
    .Select();

The above query’s usage of a subquery is pretty worthless but it demonstrates the possibility of referencing the main query (root) in the subquery.

The new auto-mapping syntax:

IMapper mapper = new AutoMapperMapper();

Mapping.SetMapper(mapper);

var dtos = session.FlowQuery<User>()
    .Select()
    .ToMap<UserDto>();

IMapper is an interface which requires you to implement one single method:

TReturn Map<TSource, TReturn>(TSource source) where TReturn : new();

By default FlowQuery uses an IMapper which simply maps properties which have the same name and the same type in both source and destination.

Enjoy! (:

/Niklas Källander

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

Introducing NHibernate.FlowQuery

Hi all NHibernate users,

Today I finally decided to publish my very close-to-heart wrapper for the NHibernate ICriteria API. NHibernate.FlowQuery (FlowQuery) is based on expression trees and hence provides you with a refactoring friendly interface that can be completely free from “magic strings” even if you have mapped NHibernate to private or protected members in your entities.

FlowQuery provides you with an easy-to-use API for revealing hidden members by configurable conventions that can be altered on the fly. The API also contains auto-mapping functionality to map your query directly into a view-model instead of having you map it yourself.

Examples:

A simple get-all query:

var users = session.FlowQuery<User>().Select();

A simple get-all query which auto-maps public properties with the same name and type into the view-model:

var users = session.FlowQuery<User>().Select<UserViewModel>();

Restrictions:

var users = session.FlowQuery<User>()
    .Where(u => u.Firstname == "Niklas"
             && u.Lastname.StartsWith("K"))
    .Select();

The above query uses the method StartsWith which is part of the .NET String-class. FlowQuery converts this method-call to Restrictions.Like(“Lastname”, “K”, MatchMode.Start). The API also have support for Contains, EndsWith and, depending on database provider, Substring.

Joining:

UserRoleLink link = null;

var users = session.FlowQuery<User>()
    .Inner.Join(u => u.Roles, () => link)
    .Where(u => link.Role == Role.Administrator)
    .Select();

Selects only Id and Email into an anonymous type:

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

Selects only Id and Email but to the same type:

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

Selects only Firstname, Lastname and Email but by specifying type, constructor and member initializer:

var users = session.FlowQuery<User>()
    .Select(u => new UserViewModel(u.Firstname, u.Lastname)
    {
        Email = u.Email
    });

Revealing hidden members is done using either the Reveal-helper or by instantiating a custom revealer. The Reveal-helper, by default, uses the “m_”-convention.

As an example:

string name = Reveal.ByConvention(() => link.Role);

name would be “m_Role”.

The convention used by the Reveal-helper can be set using Reveal.SetConvention() which takes either a Func<string, string> or an IRevealConvention.

You can also use a custom revealer like:

IRevealer revealer = new Revealer(new UnderscoreConvention());
string name = revealer.Reveal(() => link.Role);

name would be “_Role”.

Revealing in a query:

var users = session.FlowQuery<User>()
    .Inner.Join(Reveal.ByConvention<User>(u => u.Roles), () => link)
    .Where(u => link.Role == Role.Administrator)
    .Select();

The above query reveals the hidden collection m_Roles and joins it into the query.

I think those examples will be sufficient for now. If you like what you see and/or want more information about FlowQuery leave a reply!

The API can be downloaded as a dll at: http://sourceforge.net/projects/nhflowquery/

Enjoy! (:

/Niklas Källander