Version 2.2

Documentation for NHibernate.FlowQuery (FlowQuery) version 2.2. A help file (.chm) for this version of FlowQuery can be downloaded at SourceForge.

Preface

Following are a few documentation topics describing the core functionality and diverse aspects of FlowQuery. After reading this documentation you should be fully able to use FlowQuery with ease in your own projects.

Although I aim at updating this documentation along with the implementation changes I make in the code, the documentation may not always be 100% up-to-date. I hope you can condone with this, and in the occasion you do find an issue with the documentation being out-of-sync, please let me know.

This documentation is intended to always target the latest version of FlowQuery.

Table of Contents

Basics

License

FlowQuery is licensed under the Lesser General Public License version 2.1 (LGPLv2). You can read more about the license at: http://www.gnu.org/licenses/lgpl-2.1.html.

Requirements

Getting started

It’s quite easy getting started with FlowQuery, there are three small steps:

  1. Download the FlowQuery binary from http://sourceforge.net/projects/nhflowquery/files/, and
  2. Include the dll (NHibernate.FlowQuery.dll) in your project alongside the NHibernate binaries.
  3. Add a using statement in the top of your file or namespace:
    using NHibernate.FlowQuery;

And, you’re ready to go!

How to construct a FlowQuery object

FlowQuery object can be constructed in a variety of ways, but the easiest (and proper) way is to use the extension methods FlowQuery provides on ISession and IStatelessSession. To use these extension methods you must have a using statement for the NHibernate.FlowQuery namespace in the top of your file or namespace, e.g.

using NHibernate.FlowQuery;

Example 1

This example demonstrates the easiest way to retrieve a FlowQuery object.

var query = session.FlowQuery<UserEntity>();

Example 2 (with alias)

This example demonstrates how to retrieve a FlowQuery object with an alias specified.

UserEntity alias = null;

var query = session.FlowQuery<UserEntity>(() => alias);

Example 3 (with options)

This example demonstrates how to retrieve a FlowQuery object with extra options to be run on the ICriteria object before it is executed.

var options = new FlowQueryOptions();

options.Add(criteria => criteria.SetCacheMode(CacheMode.Refresh));

var query = session.FlowQuery<UserEntity>(options);

Example 4 (with alias and options)

This example demonstrates how to retrieve a FlowQuery object with an alias specified and with extra options to be run on the ICriteria object before it is executed.

UserEntity alias = null;

var options = new FlowQueryOptions();

options.Add(criteria => criteria.SetCacheMode(CacheMode.Refresh));

var query = session.FlowQuery<UserEntity>(() => alias, options);

Note that it’s currently not possible to specify any extra options for DetachedCriterias in this manner.

Projections / Selection

Simple projections using FlowQuery

Using FlowQuery you can project your underlying entities in various flexible ways. In this section you will get a full overview of the functionality FlowQuery provides for this.

Let’s start of with the basics.

Example 1

This example demonstrates the easiest way to select your entities without any particular projections.

IEnumerable<UserEntity> users = session.FlowQuery<UserEntity>()
    .Select();

Example 2 (single properties)

This example demonstrates how to project and select a single property.

IEnumerable<string> usernames = session.FlowQuery<UserEntity>()
    .Select(x => x.Username);

Example 3 (multiple properties)

This example demonstrates how to project and select multiple properties.

IEnumerable<UserEntity> users = session.FlowQuery<UserEntity>()
    .Select
    (
        x => x.Username, 
        x => x.Firstname, 
        x => x.Lastname
    );

Example 4 (using magic strings)

This example demonstrates example 2 and 3 using magic strings instead of lambda expressions.

var query = session.FlowQuery<UserEntity>();

IEnumerable<UserEntity> usernames = query
    .Select("Username");

IEnumerable<UserEntity> users = query
    .Select("Username", "Firstname", "Lastname");

Note that example 2, using this method, loses the benefit of being projected directly to string. This can be solved by specifying the type explicitly when calling Select:

Example 5 (specifying type when using magic string)

IEnumerable<string> usernames = session.FlowQuery<UserEntity>()
    .Select<string>("Username");

Example 6 (using a IProjection)

This example demonstrates example 2 using a IProjection.

IEnumerable<UserEntity> usernames = session.FlowQuery<UserEntity>()
    .Select(Projections.Property("Username"));

Note that example 2, using this method, also loses the benefit of being projected directly to string. This can be solved by specifying the type explicitly when calling Select as well:

Example 7 (specifying type when using a IProjection)

This example demonstrates example 2 using a IProjection.

IEnumerable<string> usernames = session.FlowQuery<UserEntity>()
    .Select<string>(Projections.Property("Username"));

Complex projections using FlowQuery

Sometimes you need to build complex models from your entities to return to a GUI or service layer. FlowQuery helps you project your entities into these models directly in the query.

Example 1

This example demonstrates how to build a complex model using FlowQuery.

var users = session.FlowQuery<UserEntity>()
    .Select(x => new UserDto
    {
         Fullname = x.Firstname + " " + x.Lastname,
         Username = x.Username,
         IsOnline = x.IsOnline
    });

Example 2 (with constructor)

This example demonstrates how to build a complex model and calling into a constructor using FlowQuery.

var users = session.FlowQuery<UserEntity>()
    .Select(x => new UserDto(x.Firstname + " " + x.Lastname)
    {
         Username = x.Username,
         IsOnline = x.IsOnline
    });

Example 3 (anonymous)

This example demonstrates how to build a complex anonymous model using FlowQuery.

var users = session.FlowQuery<UserEntity>()
    .Select(x => new
    {
         Fullname = x.Firstname + " " + x.Lastname,
         x.Username,
         x.IsOnline
    });

Example 4 (nested types)

This example demonstrates how to build a complex anonymous model with nested type using FlowQuery.

var users = session.FlowQuery<UserEntity>()
    .Select(x => new
    {
        Dto = new UserDto(x.Firstname + " " + x.Lastname)
        {
            Username = x.Username,
            IsOnline = x.IsOnline
        },

        x.LastLoggedInStamp,
        x.Role,
        x.Id
    });

Example 5 (subqueries)

This example demonstrates how to project the value from a subquery into your projection using FlowQuery.

UserEntity user = null;

var countQuery = session.DetachedFlowQuery<UserGroupLinkEntity>()
    .SetRootAlias(() => user)
    .Where(x => x.User.Id == user.Id)
    .Count();

var users = session.FlowQuery<UserEntity>(() => user)
    .Select(x => new
    {
        Username = x.Username,
        NumberOfGroups = Aggregate.Subquery<int>(countQuery)
    });

This feature is using NHibernate’s Projections.SubQuery feature and hence has the same limitations. The subquery must return only one value (e.g. 1 row, 1 column) for this to work.

Example 6 (type cast)

This example demonstrates how to cast the returned value from the query into the correct type for your model.

var average = session.FlowQuery<UserEntity>()
    .Select(x => new
    {
        AverageIdValue = (decimal)Aggregate.Average(x.Id)
    });

FlowQuery projections using Select Dictionary

Sometimes all you really need is a dictionary returned with a primary key and some other value. FlowQuery to the rescue:

Example 1

This example demonstrates how you can use FlowQuery to project your query directly into a Dictionary<TKey, TValue>.

var users = session.FlowQuery<UserEntity>()
    .SelectDictionary(x => x.Id, x => x.Username);

FlowQuery projections using Select Setup

There are a few moments when basic projections or advanced projections using lambda expressions (NewExpression, MemberInitExpression) isn’t convenient enough. Therefore FlowQuery also provides an alternative route which makes it possible to split the projections into smaller chunks. This projection method is called Select Setup.

Example 1

This example demonstrates how to perform a basic projection using FlowQuery‘s Select Setup functionality.

IEnumerable<UserDto> users = session.FlowQuery<UserEntity>()
    .Select<UserDto>()
        .For(x => x.Fullname).Use(x => x.Firstname + " " + x.Lastname)
        .For(x => x.Username).Use(x => x.Username)
        .Select();

Note that the type projected into must have a parameterless constructor.

FlowQuery projections using Partial Select

Just like Select Setup Partial Select aims to make it possible to split projection up into smaller chunks, however, Partial Select is quite different from the workings of Select Setup.

Example 1

This example demonstrates how to perform a basic projection using FlowQuery‘s Partial Select functionality.

var selectionBuilder = session.FlowQuery<UserEntity>()
    .PartialSelect<UserDto>(x => new UserDto(x.Firstname + " " + x.Lastname));

selectionBuilder
    .Add(x => new UserDto() { Username = x.Username });

var users = selectionBuilder
    .Select();

The two lambda expressions will both be used as projections for the query. Note that only the first provided expression can be a NewExpression. Subsequent expressions must currently all be MemberInitExpressions.

Aggregations using FlowQuery

Now and then we all need to aggregate our data, fetching average this and max that. We also need to group this data by certain other values. This section will demonstrate aggregations using FlowQuery.

Example 1

var averageLogOnsPerRole = session.FlowQuery<UserEntity>()
    .Select(x => new AverageLogOnsPerRoleModel
    {
        AverageLogOns = Aggregate.Average(x.NumberOfLogOns),
        Role = Aggregate.GroupBy(x.Role)
    });

Aggregations with FlowQuery can be used by any selection method, whether simple or complex, even with Select Setup and Partial Select.

Example 2 (with Select Setup)

var averageLogOnsPerRole = session.FlowQuery<UserEntity>()
    .Select()
        .For(x => x.AverageLogOns).Use(x => Aggregate.Average(x.NumberOfLogOns))
        .For(x => x.Role).Use(x => Aggregate.GroupBy(x.Role))
        .Select();

Example 3 (with Partial Select)

var selectionBuilder = session.FlowQuery<UserEntity>()
    .PartialSelect(x => new AverageLogOnsPerRoleModel 
    { 
        AverageLogOns = Aggregate.Average(x.NumberOfLogOns) 
    });

selectionBuilder
    .Add(x => new AverageLogOnsPerRoleModel
    {
        Role = Aggregate.GroupBy(x.Role)
    });

var averageLogOnsPerRole = selectionBuilder
    .Select();

Inferred GroupBy

Worth noting is that FlowQuery as of version 2.1 infers the usage of GroupBy for all non-grouped and non-aggregated projections. This makes it possible to write the above examples like this instead:

Example 4 (Example 1 with inferred GroupBy)

var averageLogOnsPerRole = session.FlowQuery<UserEntity>()
    .Select(x => new AverageLogOnsPerRoleModel
    {
        AverageLogOns = Aggregate.Average(x.NumberOfLogOns),
        Role = x.Role
    });

Example 5 (Example 2 with inferred GroupBy)

var averageLogOnsPerRole = session.FlowQuery<UserEntity>()
    .Select()
        .For(x => x.AverageLogOns).Use(x => Aggregate.Average(x.NumberOfLogOns))
        .For(x => x.Role).Use(x => x.Role)
        .Select();

Example 6 (Example 3 with inferred GroupBy)

var selectionBuilder = session.FlowQuery<UserEntity>()
    .PartialSelect(x => new AverageLogOnsPerRoleModel 
    { 
        AverageLogOns = Aggregate.Average(x.NumberOfLogOns) 
    });

selectionBuilder
    .Add(x => new AverageLogOnsPerRoleModel
    {
        Role = x.Role
    });

var averageLogOnsPerRole = selectionBuilder
    .Select();

Separate GroupBy

As of version 2.1 FlowQuery also supports specifying GroupBy columns without projecting them. This feature comes in handy when projecting subqueries (see subquery projection example here).

Example 7 (Example 1 with separate GroupBy)

var averageLogOnsPerRole = session.FlowQuery<UserEntity>()
    .GroupBy(x => x.Role)
    .Select(x => new AverageLogOnsPerRoleModel
    {
        AverageLogOns = Aggregate.Average(x.NumberOfLogOns),
        Role = x.Role
    });

Now we explicitly state that we want to project/select the Role property. We could also skip this entirely:

Example 8 (Example 1 with separate GroupBy without projecting GroupBy column)

var averageLogOnsPerRole = session.FlowQuery<UserEntity>()
    .GroupBy(x => x.Role)
    .Select(x => new AverageLogOnsPerRoleModel
    {
        AverageLogOns = Aggregate.Average(x.NumberOfLogOns)
    });

This will result in AverageLogOns being the only selected value although the data will be grouped by Role just like with the previous examples.

Distinct or Indistinct?

Sometimes it is necessary to get a distinct result set. This is not a problem using FlowQuery.

Example 1

var roles = session.FlowQuery<UserEntity>()
    .Distinct()
    .Select(x => x.Role);

By using Distinct() we tell FlowQuery that any projection requested later should be distinct. To reset this behavior you can call Indistinct():

Example 2 (indistinct)

var query = session.FlowQuery<UserEntity>();

query.Distinct();

//... code ...//

var roles = query
    .Indistinct()
    .Select(x => x.Role);

This can be very useful if you are retrieving a pre-used distinct query from a base method in your project and distinctness is not needed in the new context.

Count projections using FlowQuery

Being able to get a row count for a query is quite important and it couldn’t be much easier than it is with FlowQuery:

Example 1

int count = session.FlowQuery<UserEntity>()
    .Count();

This query returns the number of users in the database. Sometimes you want to count the number of a particular thing, let’s say the number of different roles being used:

Example 2

int count = session.FlowQuery<UserEntity>()
    .Distinct()
    .Count(x => x.Role);

Limit, Skip, Take

Pagination, paging, (whatever one calls it these days) is a very common pattern in most projects. You don’t want to load up the entire result set to the GUI of your application and hence you need to split the result set into smaller chunks. To do this with FlowQuery you have three methods available called Limit, Skip, and Take. Those of you familiar with MySQL should understand the concepts of Limit, and those of you familiar with System.Linq should understand the concepts of Skip and Take.

Example 1

var users = session.FlowQuery<UserEntity>()
    .Limit(3, 1)
    .Select();

This query tells FlowQuery to take only 3 records and to skip 1 (skip is normally combined with some kind of Order By statement). Limit also have an overload only taking one argument for telling how many records to take.

Example 2

var users = session.FlowQuery<UserEntity>()
    .Limit(3)
    .Select();

Now we still take 3 records but we do not skip any. These queries can also be achieved using Skip and Take.

Example 3

var users = session.FlowQuery<UserEntity>()
    .Skip(1)
    .Take(3)
    .Select();

Example 4

This example demonstrates how to clear any limit that has been set on the FlowQuery object.

var query = session.FlowQuery<UserEntity>()
    .Skip(1)
    .Take(3);

query
    .ClearLimit();

This feature can come in handy if you reuse a query and for some reason don’t need the limit anymore but want to keep other parts of the old query intact.

Any()

FlowQuery provides a few nice shortcuts for determining if any rows (matching a filter) exists or not. These are used through the overloads of Any().

Example 1

This example demonstrates the use of Any() without any parameters.

var any = session.FlowQuery<UserEntity>()
    .Any();

This query would be the equivalent of:

var any = session.FlowQuery<UserEntity>()
    .Take(1)
    .Count() > 1;

You can also specify a filter (restriction) to Any():

Example 2

var anyOnlineUsers = session.FlowQuery<UserEntity>()
    .Any(x => x.IsOnline);

You can read more about filters (restrictions) using FlowQuery here.

Miscellaneous projections using FlowQuery

FlowQuery provides a few other convenient projection possibilities, following is a few of them.

Example 1 (comparison)

This example demonstrates comparison projections using FlowQuery

var users = session.FlowQuery<UserEntity>()
    .Select(x => new
    {
        IsAdministrator = x.Role == RoleEnum.Administrator
    });

Note that all standard comparisons are available >=, <=, !=, >, <, etc.

Example 2 (ternary)

This example demonstrates use of the ternary operator in FlowQuery

var users = session.FlowQuery<UserEntity>()
    .Select(x => new
    {
        IsAdministrator = x.Role == RoleEnum.Administrator 
            ? true 
            : false
    });

Note that you could return any type from the ternary expressions, other projections as well.

Example 3 (ternary with projections)

This example demonstrates use of the ternary operator with other projections in FlowQuery

var users = session.FlowQuery<UserEntity>()
    .Select(x => new
    {
        Name = x.Role == RoleEnum.Administrator
            ? x.Lastname + ", " + x.Firstname
            : x.Username
    });

Example 4 (coalesce)

This example demonstrates use of the coalesce operator with FlowQuery

var users = session.FlowQuery<UserEntity>()
    .Select(x => new
    {
        x.Username,
        Value = x.Password ?? x.Username
    });

Example 5 (Substring)

This example demonstrates use of Substring with FlowQuery

var users = session.FlowQuery<UserEntity>()
    .Select(x => new
    {
        Initials = (x.Firstname.Substring(0, 1) + x.Lastname.Substring(0, 1)),
        x.Firstname,
        x.Lastname
    });

Example 6 (StartsWith, EndsWith, Contains)

This example demonstrates use of StartsWith, EndsWith, and Contains with FlowQuery

var users = session.FlowQuery<UserEntity>()
    .Select(x => new
    {
        ContainsO = x.Firstname.Contains("o"),
        StartsWithN = x.Firstname.StartsWith("n"),
        EndsWithN = x.Firstname.EndsWith("n")
    });

Restrictions

Simple restrictions using FlowQuery

Using FlowQuery you can restrict (filter) your underlying entities in various flexible ways. In this section you will get a full overview of the functionality FlowQuery provides for this.

The methods for making restrictions are called Where, And, and RestrictByExample. Note that And is a direct alias for Where and is hence not demonstrated in the examples below. Any() which is described above also have the same overloads as Where.

Let’s start of with the basics.

Example 1

This example demonstrates how to filter your query on a Boolean property on the underlying entity.

var onlineUsers = session.FlowQuery<UserEntity>()
    .Where(x => x.IsOnline)
    .Select();

Example 2

This example demonstrates how to combine multiple filters in one.

var onlineAdministrators = session.FlowQuery<UserEntity>()
    .Where(x => x.IsOnline && x.Role == RoleEnum.Administrator)
    .Select();

Example 3

This example also demonstrates how to combine multiple filters in one, but using logical OR instead of logical AND.

var onlineOrStandardUsers = session.FlowQuery<UserEntity>()
    .Where(x => x.IsOnline || x.Role == RoleEnum.Standard)
    .Select();

Example 4

This example demonstrates how to filter your query using the FlowQuery IsHelper.

var administrators = session.FlowQuery<UserEntity>()
    .Where(x => x.Role, Is.EqualTo(RoleEnum.Administrator))
    .Select();

Example 5

This example also demonstrates how to use the FlowQuery IsHelper but uses another method on the helper.

var privilegedUsers = session.FlowQuery<UserEntity>()
    .Where(x => x.Role, Is.In(RoleEnum.Administrator, RoleEnum.Webmaster))
    .Select();

Exampe 6

This example demonstrates filtering on mapped association (collection) being empty.

var users = session.FlowQuery<UserEntity>()
    .Where(x => x.Groups, Is.Empty())
    .Select(x => new
    {
        Username = x.Username
    });

Complex restrictions using FlowQuery

FlowQuery also provides a few functions for a bit more complex filtering.

Example 1

This example demonstrates the use of FlowQuery‘s IsHelper in combination with a subquery (more about subqueries can be read here).

var subquery = session.FlowQuery<UserEntity>()
    .Detached()
    .Select(x => x.Id);

var users = session.FlowQuery<UserEntity>()
    .Where(x => x.Id, Is.In(subquery))
    .Select();

Example 2

Sometimes you need to use the FlowQuery IsHelper in a logical OR expression. This is not really possible in the simple Where() overloads.

This example demonstrates the use of an overload providing a WhereDelegate which makes this requirement fully possible.

var onlineOrPrivilegedUsers = session.FlowQuery<UserEntity>()
    .Where((x, where) => x.IsOnline || where(x.Role, Is.In(RoleEnum.Administrator, RoleEnum.Webmaster)))
    .Select();

Example 3

This example demonstrates filtering by Contains on a String property. (StartsWith and EndsWith are also fully functional.)

var usersHavingOInFistname = session.FlowQuery<UserEntity>()
    .Where(x => x.Firstname.Contains("o"))
    .Select();

Example 4

This example demonstrates filtering using Substring on a String property.

var usersWithNameStartingOnN = session.FlowQuery<UserEntity>()
    .Where(x => x.Firstname.Substring(0, 1) == "N")
    .Select();

Example 5

This example demonstrates the use of FlowQuery‘s IsHelper in combination with a DetachedCriteria.

var criteria = DetachedCriteria.For<UserGroupLinkEntity>()
    .Add(Restrictions.Between("Id", 2, 3))
    .SetProjection(Projections.Property("Id"));

var users = session.FlowQuery<UserGroupLinkEntity>()
    .Where(x => x.Id, Is.In(criteria))
    .Select();

Example 6

This example demonstrates filtering on a subquery result being empty.

UserEntity user = null;

var subquery = session.DetachedFlowQuery<UserGroupLinkEntity>()
    .SetRootAlias(() => user)
    .Where(x => x.User.Id == user.Id)
    .Select(x => x.Id);

var users = session.FlowQuery<UserEntity>(() => user)
    .Where(subquery, Is.Empty())
    .Select(x => new
    {
        Username = x.Username
    });

Miscellaneous restrictions using FlowQuery

There aren’t that many special filtering methods in FlowQuery but at least it supports the Example features in the ICriteria API.

Example 1

This example demonstrates filtering using RestrictByExample.

var users = session.FlowQuery<UserEntity>()
    .RestrictByExample(new UserEntity() { Firstname = "Niklas", Role = RoleEnum.Administrator }, x =>
    {
        x.ExcludeProperty(u => u.CreatedStamp);
        x.ExcludeProperty(u => u.IsOnline);
        x.ExcludeProperty(u => u.NumberOfLogOns);
        x.ExcludeZeroes();
        x.ExcludeNulls();
    })
    .Select();

Joins

How to join using FlowQuery

With FlowQuery joining is performed through the properties Inner, LeftOuter, RightOuter, and Full. All these properties have a method called Join with a couple of overloads.

To join an association you must first create an alias for the association path to be used later in the query.

Example 1

UserGroupLinkEntity linkAlias = null;

Example 2

This example demonstrates how the created alias is used in the simplest of the Join overloads.

UserGroupLinkEntity linkAlias = null;

var users = session.FlowQuery<UserEntity>()
    .Inner.Join(x => x.Groups, () => linkAlias)
    .Select();

Example 3

This example demonstrates a Join overload where we can specify an extra ON-clause restriction.

UserGroupLinkEntity linkAlias = null;

var users = session.FlowQuery<UserEntity>()
    .Inner.Join(x => x.Groups, () => linkAlias, () => linkAlias.Group.Id == 1)
    .Select();

Example 4

This example demonstrates a Join overload where we can specify an extra ON-clause restriction and a IRevealConvention.

UserGroupLinkEntity linkAlias = null;
GroupEntity groupAlias = null;

var users = session.FlowQuery<UserEntity>()
    .Inner.Join(x => x.Groups, () => linkAlias)
    .Inner.Join(() => linkAlias.Group, () => groupAlias, () => groupAlias.Id == 1, new CustomConvention(x => "m_" + x))
    .Select();

The IRevealConvention is used to tell FlowQuery to map the association as linkAlias.m_Group instead of linkAlias.Group. Read more about IRevealConvention and its usages here.

Example 5

This example demonstrates a Join overload where we can specify a IRevealConvention without specifying an extra ON-clause restriction.

UserGroupLinkEntity linkAlias = null;
GroupEntity groupAlias = null;

var users = session.FlowQuery<UserEntity>()
    .Inner.Join(x => x.Groups, () => linkAlias)
    .Inner.Join(() => linkAlias.Group, () => groupAlias, new CustomConvention(x => "m_" + x))
    .Select();

As in Example 4 the IRevealConvention is used to tell FlowQuery to map the association as linkAlias.m_Group instead of linkAlias.Group. Read more about IRevealConvention and its usages here.

Example 6

This example demonstrates how to clear all joins that have been made on your FlowQuery object.

UserGroupLinkEntity linkAlias = null;

var query = session.FlowQuery<UserEntity>()
    .Inner.Join(x => x.Groups, () => linkAlias, () => linkAlias.Group.Id == 1);

query
    .ClearJoins();

This feature can come in handy if you reuse a query and for some reason don’t need the joins anymore, or simply want to reset them from scratch and keep other parts of the old query intact. Note that doing this may corrupt any projection or restriction that also have been set on the query.

Orders

How to order your selection using FlowQuery

All orders are set through the OrderBy and OrderByDescending methods on your FlowQuery object. All orders are executed in the database in the exact order they’ve been set on the query.

Here’s a simple example:

Example 1

This example demonstrates a query first ordered descending on IsOnline then ascending on Role and then ascending on Username.

var users = session.FlowQuery<UserEntity>()
    .OrderByDescending(x => x.IsOnline)
    .OrderBy(x => x.Role)
    .OrderBy(x => x.Username)
    .Select();

You can also use a Boolean flag to determine whether or not to order ascending:

Example 2

This example demonstrates the exact same thing as Example 1 but uses another overload for setting the orders.

var users = session.FlowQuery<UserEntity>()
    .OrderBy(x => x.IsOnline, false)
    .OrderBy(x => x.Role, true)
    .OrderBy(x => x.Username, true)
    .Select();

FlowQuery also provides a complex ordering feature which lets you order by a property on the projected model.

Let’s demonstrate:

Example 3

var users = session.FlowQuery<UserEntity>()
    .OrderBy(x => x.IsOnline)
    .OrderBy<UserDto>(x => x.SomeValue)
    .Select(x => new UserDto()
    {
        Fullname = x.Firstname + " " + x.Lastname,
        Username = x.Username,
        IsOnline = x.IsOnline,
        SomeValue = x.Username.Substring(0, 3) + x.Firstname.Substring(0, 3) + x.Lastname.Substring(0, 3)
    });

Note that this functionality requires that you project into the property you sort on and that the projected type is the same as the one you are ordering by. However, you may combine this feature with regular ordering as can be concluded by the above example.

Example 4

This example demonstrates how to order by magic string and IProjection projections.

var users = session.FlowQuery<UserEntity>()
    .OrderByDescending("IsOnline")
    .OrderBy(Projections.Property("Role"))
    .OrderBy("Username")
    .Select();

Example 5

This example demonstrates how to clear all orders that have been made on your FlowQuery object.

var query = session.FlowQuery<UserEntity>()
    .OrderBy(x => x.IsOnline, false)
    .OrderBy(x => x.Role, true)
    .OrderBy(x => x.Username, true)

query
    .ClearOrders();

This feature can come in handy if you reuse a query and for some reason don’t need the orders anymore, or simply want to reset them from scratch and keep other parts of the old query intact.

A point of interest could be that FlowQuery automatically omits orders if a query is executed in a Detached context unless Limit (Take, Skip) is also specified in the query. This is to reduce the possibility of run time errors when executing reused queries in new contexts. Read more about the different FlowQuery contexts here.

Interchangeability & reusability

What does interchangeability and reusability mean when it comes to FlowQuery?

By this I mean the possibility to switch context for FlowQuery objects and to actually reuse the queries multiple times both with and without different projections. E.g. you could potentially use the same query for fetching a small result set of models and for fetching the total row count for the entire query at the same time, without doing any major changes to the query in between.

Example 1

var query = session.FlowQuery<UserEntity>()
    .Delayed()
    .Where(x => x.IsOnline);

var count = query.Count();

var users = query
    .Take(10)
    .Select();

This query, using the benefit of the Delayed context (which will be explained further down), will fetch both (max) 10 users and the total count of users matching the filters in the query at the same time. There will only be one round-trip to the database. Note that I didn’t need to change anything between the projections, that’s reusability in my opinion.

FlowQuery contexts

There are currently three different contexts for, or types of, a FlowQuery object. These are called Immediate, Delayed, and Detached. The difference between them is mostly related to what, and how you can project your underlying entities, and also when the query is executed.

All queries are in the Immediate context by default when creating a query using session.FlowQuery<T>(). As of version 2.1 of FlowQuery it is also possible to create specific query types directly using session.DetachedFlowQuery<T>(), session.ImmediateFlowQuery<T>(), or session.DelayedFlowQuery<T>().

All methods of creating a FlowQuery object are implemented for both ISession and IStatelessSession.

NOTE: It is also possible to create a Detached FlowQuery object from a DetachedCriteria, however such FlowQuery objects will be immutable and they cannot change context/type.

Immediate

The Immediate context for, or type of, a FlowQuery object is related to the way data is fetched from the database. It is fetched immediately when running the query.

var query = session.FlowQuery<UserEntity>(); // <- this is an Immediate query object

To make a Detached or Delayed query a Immediate one you call the Immediate() method on them. E.g.

var query = ...; // <- this is a Delayed query object

var immediate = query.Immediate();

Delayed

The Delayed context for, or type of, a FlowQuery object is also related to the way data is fetched from the database. In this case, the data is fetched delayed when running the query, using NHibernate‘s MultiCriteria features.

To make a Detached or Immediate query a Delayed one you call the Delayed() method on them. E.g.

var query = ...; // <- this is a Immediate query object

var delayed = query.Delayed();

Detached

The Detached context for, or type of, a FlowQuery object is related to how the query can be used. A Detached query cannot be used to fetch data directly from the database, it can only be used in another query (as a subquery) for a restriction or similar.

To make a Delayed or Immediate query a Detached one you call the Detached() method on them. E.g.

var query = ...; // <- this is a Immediate query object

var detached = query.Detached();

You can also create a Truly Detached * query using the static DetachedFlowQuery helper (introduced in version 2.2):

var query = DetachedFlowQuery.For<UserEntity>();

However, a Truly Detached * query requires a session (ISession or IStatelessSession) to be transformed into the Delayed or Immediate contexts. Otherwise, FlowQuery will throw a InvalidOperationException. Any Detached query created using any of the extension methods for ISession, or IStatelessSession will not be Truly Detached * and can safely be transformed into the other contexts without providing a session.

var detachedQuery = DetachedFlowQuery.For<UserEntity>();

// requires a session
var immediateQuery = detachedQuery.Immediate(session); 

// ---

var detachedQuery = session.DetachedFlowQuery<UserEntity>();

// does not require a session
var immediateQuery = detachedQuery.Immediate();

// ---

var delayedQuery = session.DelayedFlowQuery<UserEntity>();

var detachedQuery = delayedQuery.Detached();

// does not require a session
var immediateQuery = detachedQuery.Immediate();

* Truly Detached means that there are no references to either ISession, or IStatelessSession involved in creating the query. The query is created without any knowledge of how to generate a ICriteria instance for the underlying source entity.

What actually happens when I switch context on my queries?

When you switch context on your queries using Detached(), Delayed(), or Immediate() you actually receive a new object based on the old one. The new object usually is a little bit different but syntax-wise it is pretty much the same.

The biggest difference is between a query in the Detached context and one in any of the other contexts. The Detached context doesn’t have as many alternatives for projecting data as the other contexts do. This is because it should never really be used to build a complex model, it is designed to be used as a subquery (at least at the moment).

When you switch context between Immediate and Delayed only some of the signatures change. Mostly for the methods Count(), Any() and SelectDictionary() which, in the Delayed context, instead of returning their respective types, as in the Immediate context, returns a Lazy<T>. This is to provide the mechanism for delaying the execution of the queries until a later point in time, when the actual value is required by your code.

Features to support reusability

FlowQuery provides a subset of features to improve the reusability experience. The idea behind most of these features is to be able to clear or reset parts of a query before reusing it. Following are a couple of examples demonstrating these features a bit more closely.

Copy()

This example demonstrates how to copy a FlowQuery object before changing it further. This to be able to go multiple ways with the same query base.

var baseQuery = session.DelayedFlowQuery<UserEntity>()
    .Where(x => x.IsOnline)
    .OrderBy(x => x.Username);

var onlineAdministrators = baseQuery
    .Copy()
    .Where(x => x.Role == RoleEnum.Administrator)
    .Select();

var onlineNonAdministrators = baseQuery
    .Copy()
    .Where(x => x.Role != RoleEnum.Administrator)
    .Select();

In the above example both result sets will have been filtered using baseQuery‘s restrictions on top of their own specific restrictions. This query can be written in multiple different ways using FlowQuery. For instance, by using ClearRestrictions().

ClearRestrictions()

This example demonstrates how to re-write the above example using ClearRestrictions() instead of Copy().

var query = session.DelayedFlowQuery<UserEntity>()
    .OrderBy(x => x.Username);

var onlineAdministrators = query
    .Where(x => x.IsOnline && x.Role == RoleEnum.Administrator)
    .Select();

var onlineNonAdministrators = query
    .ClearRestrictions()
    .Where(x =>  x.IsOnline && x.Role != RoleEnum.Administrator)
    .Select();

Clear*()

FlowQuery provides a couple of other methods to clear/reset parts of the query, here is a short example demonstrating all of them:

var query = session.FlowQuery<UserEntity>();

// clear fetch instructions specified using query.Fetch(..)
query.ClearFetches();

// clear group by columns specified using query.GroupBy(..)
query.ClearGroupBys();

// clear joins specified using query.Inner/Full/LeftOuter/RightOuter
query.ClearJoins();

// clear limit/take/skip specified using query.Take(..)/Skip(..)/Limit(..)
query.ClearLimit();

// clear locks specified using query.Lock(..)
query.ClearLocks();

// clear orders specified using query.OrderBy(..)/OrderByDescending(..)
query.ClearOrders();

// clear restrictions specified using query.Where(..)/And(..)/RestrictByExample(..)
query.ClearRestrictions();

// clear timeout specified using query.Timeout(..)/TimeoutAfter(..)
query.ClearTimeout();

Subqueries in FlowQuery

As you might have understood from the above information about interchangeability and reusability, any and all queries can potentially be used as a subquery in FlowQuery. The only requirement is that the context in which they are executed and used, is Detached.

Subqueries, or queries in Detached context, can be used in restrictions and projections of other queries:

Other Utilities

As of FlowQuery version 2.1 a couple of utility features have been made available directly from your FlowQuery object instead of having to resort to using FlowQueryOptions. Following are a couple of examples demonstrating these features.

SetFetchMode() using FlowQuery

Setting FetchMode on an association determines how NHibernate should load the association path. NHibernate is lazy by nature but sometimes you want to receive some of your associations directly without a separate round-trip to the database, due to lazy-loading. Sometimes fetching data eagerly is the common case (using mapping configuration) and you want a particular query not to fetch a specific association eagerly.

This is where Fetch() come in handy.

Example 1

The following example demonstrates how to specify fetch modes using FlowQuery:

var users = session.FlowQuery<UserEntity>()
    .Fetch(x => x.Groups).Eagerly()
    .Select();

To specify FetchMode on deeper association chains you have different options depending on the associations.

For many-to-one or one-to-one associations you just have to keep on going (e.g. Association.Association.Association, and so on, and so forth). For one-to-many or many-to-many associations you need to either use a string or specify an alias.

Example 2

The following example demonstrates how to specify FetchMode for a deeper association chain without resorting to using a string. The example also demonstrates both mentioned cases (one-to-one/many-to-one and one-to-many/many-to-many).

UserGroupLinkEntity groupLink = null;

var users = session.FlowQuery<UserEntity>()
    .Fetch(x => x.Groups, () => groupLink).Eagerly()
    .Fetch(x => groupLink.Group.Customers).Eagerly()
    .Select();

Example 3

By using a string the above example can be shortened down to just one Fetch() statement:

var users = session.FlowQuery<UserEntity>()
    .Fetch("Groups.Group.Customers").Eagerly()
    .Select();

Example 4

You can also clear all previously specified fetch instructions using ClearFetches(). The following example demonstrates this feature:

UserGroupLinkEntity groupLink = null;

var query = session.FlowQuery<UserEntity>()
    .Fetch(x => x.Groups, () => groupLink).Eagerly()
    .Fetch(x => groupLink.Group.Customers).Eagerly();

// ... code ...

var users = query
    .ClearFetches()
    .Select();

When specifying FetchMode using Fetch(..) you have four options:

  • WithJoin() which corresponds to FetchMode.Join
  • WithSelect() which corresponds to FetchMode.Select
  • Eagerly() which corresponds to FetchMode.Eager and is logically the same as WithJoin() or FetchMode.Join
  • Lazily() which corresponds to FetchMode.Lazy and is logically the same as WithSelect() or FetchMode.Select

SetCacheable() using FlowQuery

Some systems and applications handle a lot of data, where parts of the data are pretty much static. It’s a good (and common) practice to cache the results of queries for such data.

In NHibernate this is called second-level cache, and ICriteria provides a couple of methods for configuring the so called query cache; SetCacheable(bool), SetCacheRegion(string), and SetCacheMode(CacheMode).

As of version 2.1, FlowQuery provides the same features through Cacheable(). Following are a couple of examples demonstrating these features.

Example 1

Simplest Cacheable() example using FlowQuery:

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

Example 2

Cacheable() example with specified cache region using FlowQuery:

var users = session.FlowQuery<UserEntity>()
    .Cacheable("Region1")
    .Select();

Example 3

Cacheable() example with specified cache region and CacheMode using FlowQuery:

var users = session.FlowQuery<UserEntity>()
    .Cacheable("Region1", CacheMode.Normal)
    .Select();

Example 4

Cacheable() example with only CacheMode specified using FlowQuery:

var users = session.FlowQuery<UserEntity>()
    .Cacheable(CacheMode.Normal)
    .Select();

Example 5

Example of how to turn query cache off using FlowQuery:

var users = session.FlowQuery<UserEntity>()
    .Cacheable(false)
    .Select();

NOTE: in order for second-level cache to work you must also configure NHibernate for it when building your ISessionFactory.

SetReadOnly() using FlowQuery

Following are a couple of examples demonstrating ICriteria.SetReadOnly(bool) using FlowQuery‘s ReadOnly().

Example 1

Simplest ReadOnly() example using FlowQuery:

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

Example 2

ReadOnly() example turning read-only off using FlowQuery:

var users = session.FlowQuery<UserEntity>()
    .ReadOnly(false)
    .Select();

From NHibernate docs:

Read-only entities can be modified, but changes are not persisted. They are not dirty-checked and snapshots of persistent state are not maintained.

When a proxy is initialized, the loaded entity will have the same read-only setting as the uninitialized proxy has, regardless of the session’s current setting.

The read-only setting has no impact on entities or proxies returned by the criteria that existed in the session before the criteria was executed.

SetLockMode() using FlowQuery

Following are a couple of examples demonstrating ICriteria.SetLockMode(LockMode) using FlowQuery‘s Lock().

Example 1

Example of locking root entity for Write:

var users = session.FlowQuery<UserEntity>()
    .Lock().Write()
    .Select();

Example 2

Example of locking root entity for Write using alias:

UserEntity user = null;

var users = session.FlowQuery<UserEntity>(() => user)
    .Lock(() => user).Write()
    .Select();

Example 3

Example of locking joined entity for Write:

UserGroupLinkEntity groupLink = null;

var users = session.FlowQuery<UserEntity>()
    .Inner.Join(x => x.Groups, () => groupLink)
    .Lock(() => groupLink).Write()
    .Select();

Example 4

Example of locking joined entity for Write using string alias:

UserGroupLinkEntity groupLink = null;

var users = session.FlowQuery<UserEntity>()
    .Inner.Join(x => x.Groups, () => groupLink)
    .Lock("groupLink").Write()
    .Select();

Example 5

Example of clearing locks:

UserGroupLinkEntity groupLink = null;

var query = session.FlowQuery<UserEntity>()
    .Inner.Join(x => x.Groups, () => groupLink)
    .Lock().Write()
    .Lock(() => groupLink).Write();

// ... code ...

var users = query
    .ClearLocks()
    .Select();

When specifying LockMode using Lock(..) you have six options (below quotes are from NHibernate docs):

  • Force() which corresponds to LockMode.Force

    Similar to Upgrade except that, for versioned entities, it results in a forced version increment.

  • None() which corresponds to LockMode.None

    No lock required.

  • Read() which corresponds to LockMode.Read

    A shared lock.

  • Upgrade() which corresponds to LockMode.Upgrade

    An upgrade lock.

  • UpgradeNoWait() which corresponds to LockMode.UpgradeNoWait

    Attempt to obtain an upgrade lock, using an Oracle-style SELECT ... FOR UPGRADE NOWAIT.

  • Write() which corresponds to LockMode.Write

    A Write lock is obtained when an object is updated or inserted.

SetTimeout() using FlowQuery

Long running queries is never desirable in an application and NHibernate provides you with features to cope with this. To limit the execution time for ICriteria queries you can use the SetTimeout() method. FlowQuery exposes this feature through the Timeout() method.

Example 1

This example demonstrates how to set a timeout using FlowQuery:

var users = session.FlowQuery<UserEntity>()
    .Timeout(10)
    .Select();

Example 2

This example demonstrates how to clear timeout set on a query timeout:

var query = session.FlowQuery<UserEntity>()
    .Timeout(10);

// ... code ...

var users = query
    .ClearTimeout()
    .Select();

NOTE: Timeouts are specified in seconds.

SetFetchSize() using FlowQuery

Sometimes you need to retrieve a lot of data from the database, but loading the entire result set directly into memory isn’t desirable. By using SetFetchSize() on your ICriteria query, NHibernate is able to specify an optimization hint to the underlying database driver.

The purpose behind this setting is not to specify the definite number of results to return, but merely the number of results to return per round-trip.

Let’s say that a query returns a total of 100, 000 (one hundred thousand) rows. Instead of loading the entire result set into memory, when specifying 1, 000 (one thousand) in SetFetchSize() the database driver can load 1, 000 (one thousand) rows at a time. When the first set is used up a new set of 1, 000 (one thousand) rows is retrieved.

This way memory usage can be more optimized, however, the extra round-trips to the database might cause a performance hit.

Example 1

This example demonstrates how to set fetch size using FlowQuery:

var users = session.FlowQuery<UserEntity>()
    .FetchSize(50)
    .Select();

Example 2

This example demonstrates how to reset/clear fetch size using FlowQuery:

var query = session.FlowQuery<UserEntity>()
    .FetchSize(50);

// ... code ...

var users = query
    .FetchSize(0)
    .Select();

Disclaimer: The NHibernate documentation about SetFetchSize() is pretty scarce. And so, this is merely my conclusions after reading different blogs, forums, etc. and my conclusions might not be accurate at all.

SetComment() using FlowQuery

NHibernate provides a feature to add a comment to your ICriteria query. This feature is exposed in FlowQuery through the Comment() method.

Example 1

This example demonstrates how to add a comment to your query using FlowQuery:

var users = session.FlowQuery<UserEntity>()
    .Comment("This is an example comment.")
    .Select();

Example 2

This example demonstrates how to reset/clear the comment set on your query using FlowQuery:

var query = session.FlowQuery<UserEntity>()
    .Comment("This is an example comment.");

// ... code ...

var users = query
    .Comment(null)
    .Select();

Miscellaneous

Criteria Builder

As of version 2.2 of FlowQuery, the static CriteriaHelper class has been replaced with a new interface called ICriteriaBuilder. The idea is to make it possible to roll your own criteria builder for making different alterations to the generated ICriteria instances on a overall level.

Example 1

The criteria builder can be specified in two different ways, either globally:

FlowQueryOptions.GlobalCriteriaBuilder = new MyCustomCriteriaBuilder();

Example 2

… or locally, per query:

var options = new FlowQueryOptions
{
    CriteriaBuilder = new MyCustomCriteriaBuilder()
};

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

Why would you want to roll your own criteria builder? No idea, why not? Perhaps you don’t like how the default implementation in FlowQuery is doing things or perhaps you need to tweak something when the ICriteria is created. Or, perhaps you have created your own query transformation/context and need to do stuff on the ICriteria instances to support it?

This concept will probably be developed further and the methods to implement and/or override will definitely become smaller and more separated in future versions (at the moment you need to re-implement everything, unless you are simply extending the current implementation).

Error Suppression

As of version 2.2 of FlowQuery, it is possible to suppress errors that otherwise will be thrown when ordering by a projection using OrderBy<TProjection>(..) and OrderByDescending<TProjection>(..).

The errors referred to are:

  • When the type specified by TProjection is not the same as the type of the actual projection.
  • When the property specified on the projection type isn’t found in the projection list.

Example 1

You have two options for suppressing errors, either you do it globally:

FlowQueryOptions.GloballySuppressOrderByProjectionErrors = true;

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

Example 2

… or you do it locally, per query:

var options = new FlowQueryOptions
{
    ShouldSuppressOrderByProjectionErrors = true
};

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

Example 3

Well, now both above examples would run perfectly fine. So as a simple example you might think it a great idea to populate the full name value through the constructor on your dto (data transfer object) instead of setting the property explicitly (which, by the way, is perfectly do-able using FlowQuery, see a “complex” projection example using constructor):

FlowQueryOptions.GloballySuppressOrderByProjectionErrors = true;

var users = session.FlowQuery<UserEntity>()
    .OrderBy<UserDto>(x => x.Fullname)
    .Select(x => new UserDto(x.Firstname + " " + x.Lastname));

If global suppression weren’t specified in the above example a InvalidOperationException would be thrown when building the criteria. Since global suppression is specified FlowQuery simply ignores the ordering for the specific property instead.

Reveal Conventions

NHibernate does not only allow you to map public properties and members of your entities, you can basically map anything. With this in mind, you might think that you would need to use magic strings in order to use your members as associations in your queries?

Introduction to FlowQuery Reveal Conventions

Not necessarily. FlowQuery have a concept called Reveal Conventions which automatically finds the hidden members using conventions on public members (if such members exist).

In many cases when you have a private member you also have a public property which provides you with a read-only version of the value in the private member. We might have the following class:

public class UserEntity
{
    private IList<Role> m_Roles;

    public UserEntity()
    {
        m_Roles = new List<Role>();
    }

    public virtual IEnumerable<Role> Roles
    {
        get { return from role in m_Roles select role; }
    }
}

For the sake of simplicity I’ve omitted public methods for adding/removing/clearing roles etc. Well, well, let’s say we’ve mapped the Roles collection on the private member. This would normally mean that any querying on the collection would have to be made on the private member as well. And, since the member isn’t public we can’t benefit from lambdas and get rid of magic strings. Here’s where the Reveal Conventions comes in handy.

By using a IRevealConvention we can use the public member Roles to get to the private member without the use of magic strings. Here’s an example:

Role role = null;

var userRoles = session.FlowQuery<UserEntity>()
    .Inner.Join(x => x.Roles, () => role, new CustomConvention(x => "m_" + x))
    .Select(x => new RoleDto()
    {
        Id = role.Id,
        Name = role.Name
    });

What happens in the above query is that FlowQuery uses the IRevealConvention provided to transform the association path to the correct value. E.g. instead of “Roles” we use “m_Roles”.

Setting default IRevealConvention

You can set a default IRevealConvention using the RevealHelper. E.g.

Reveal.SetDefaultConvention(x => "m_" + x);

Which you later can use in your code by the shorthand:

Reveal.DefaultConvention

This to reduce the amount of places were you have to define your convention. Using the default convention would alter the above example into this:

Role role = null;

var userRoles = session.FlowQuery<UserEntity>()
    .Inner.Join(x => x.Roles, () => role, Reveal.DefaultConvention)
    .Select(x => new RoleDto()
    {
        Id = role.Id,
        Name = role.Name
    });

You can also use the RevealHelper to reveal members in other contexts of FlowQuery:

var niklas = session.FlowQuery<UserEntity>()
    .Where(Reveal.ByConvention<UserEntity>(x => x.Username), Is.EqualTo("Niklas"))
    .Select();

Auto Mapping

When you get a selection from FlowQuery it is always of the type FlowQuerySelection<T>. This is a somewhat complex structure that implements IEnumerable<T>. However, it also implements a few additional features. One of these features is the method ToMap<T>(). This method maps the type of the selection into the new specified type and returns a new FlowQuerySelection<T>. E.g.

FlowQuerySelection<UserEntity> users = session.FlowQuery<UserEntity>()
    .Select();

FlowQuerySelection<UserDto> dtos = users.ToMap<UserDto>();

FlowQuery does have a simple implementation for this mapping functionality using reflection. This mapper simply maps properties that matches by type and name from the source type to the destination type. However, if you need more advanced mapping functionality you can configure FlowQuery to use any other mapper you’d like (as long as they implement the IMapper interface, found in NHibernate.FlowQuery.AutoMapping). This can be achieved using the MappingHelper:

IMapper myMapper = new MyMapper();

Mapping.SetMapper(myMapper);

Note that the ToMap<T>() method on FlowQuerySelection<T> will take Delayed context into consideration when mapping your result. Even if you’ve mapped the result, as far as you can see, the query won’t be executed until you start using the data, mapped or not.

Epilogue

You will come a long way using FlowQuery and you will come even further in future versions. I’m constantly experimenting with new ways to improve upon what I already got, but I can’t think of everything. If you have an idea of something that would be a nice addition to a future version of FlowQuery, please don’t hesitate to contact me with a description (and perhaps even a patch?) and I will try to make the best of it.

Enjoy! (:

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: