Castle Windsor - resolve dependencies on an existing object instance

Print Content | More

Being able to configure an existing object resolving all its dependencies sometimes can be useful, but it can indeed be dangerous because you are mixing two different techniques of creating and managing objects lifecycles, I will not discuss why this can be troublesome, because it’s a very long and complex discussion to do in this short blog post.

Nonetheless it happened in a couple of situation that I needed to configure an already existing instance of an object, but Castle Windsor doesn’t have direct support for this scenario (Unity has a Buildup() function to do the job).

The easiest thing you can do is rely on Reflection to cycle through all your properties and ask the container to resolve the dependencies; here are a couple of extension methods that work well for me (they can be further extended to cover some more cases):

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Castle.MicroKernel.Resolvers.SpecializedResolvers;
using Castle.Windsor;

public static class Extensions
{
	/// <summary>
	/// Determines whether the specified type is a generic collection.
	/// </summary>
	/// <param name="type">The type.</param>
	public static bool IsGenericCollection(this Type type)
	{
		if (type == null)
		{
			throw new ArgumentNullException("type");
		}
		return type.GetInterfaces().Where(@interface => @interface.IsGenericType).Any(@interface => @interface.GetGenericTypeDefinition() == typeof(ICollection<>));
	}

	/// <summary>
	/// Adds the CollectionResolver to the container.
	/// </summary>
	/// <param name="container">The container.</param>
	/// <param name="allowEmptyCollections">if set to <c>true</c> [allow empty collections].</param>
	public static void AddCollectionResolver(this IWindsorContainer container, bool allowEmptyCollections)
	{
		container.Kernel.Resolver.AddSubResolver(new CollectionResolver(container.Kernel, allowEmptyCollections));
		_useCollectionResolver = true;
	}

	/// <summary>
	/// I havent found an easy way to check if a SubResolver was added to the kerner, so just register it through our extension method and
	/// use a static bool variable to do the check
	/// </summary>
	private static bool _useCollectionResolver;

	public static void ResolveDependencies(this IWindsorContainer container, object objToConfigure)
	{
		// get all the properties through reflection
		PropertyInfo[] props = objToConfigure.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
		foreach (var info in props)
		{
			object o = info.GetValue(objToConfigure, null);
			// skip the object is it already contains a value of any sort
			if (o != null) continue;
			if (_useCollectionResolver && info.PropertyType.IsGenericCollection())
			{
				o = container.ResolveAll(info.PropertyType.GetGenericArguments()[0]);
			}
			else if ((info.PropertyType.IsInterface) || (info.PropertyType.IsClass))
			{
				// try to resolve the related type if the component knows it
				if (container.Kernel.HasComponent(info.PropertyType))
					o = container.Resolve(info.PropertyType);
			}
			if (o != null)
				info.SetValue(objToConfigure, o, null);
		}
	}

	public static void ReleaseDependencies(this IWindsorContainer container, object objToConfigure)
	{
		// get all the properties through reflection
		PropertyInfo[] props = objToConfigure.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
		foreach (var info in props)
		{
			object o = info.GetValue(objToConfigure, null);
			if (o == null) continue;
			if (info.PropertyType.IsGenericCollection())
			{
				ICollection c = o as ICollection;
				if (c != null)
					foreach (var obj in c)
						container.Release(obj);
			}
			else if ((info.PropertyType.IsInterface) || (info.PropertyType.IsClass))
			{
				container.Release(o);
			}
		}
	}
}

I am using the newly released Castle Windsor 2.5 so these methods can also take advantage of the new CollectionResolver (if you configure the container to use it).

However carefully consider this advice: if you want to use this method, you should be aware of what you are doing and you should also know all your object structures, hierarchies and dependencies! Introducing unwanted side effects can be extremely easy: just think if one of your subcomponents have a dependency to the object you are configuring...who is responsible for resolving those dependencies? The container itself can’t, because it does not know the object, or in the worst case it will create a duplicate of the object and inject it in the ‘child’ elements.


Castle windsor

0 comments

NHibernate - prepare_sql and some considerations on mapping very long string fields

Print Content | More

Recently I’ve switched some of my applications from NHibernate 2.1.x to NHibernate 3, everything was working fine until I encountered a ‘strange behavior’ with the mapping of long string fields (those that should be mapped to nvarchar(max) and varchar(max)...yes I use Microsoft Sql Server as my database engine): using the standard mapping the field are correctly mapped to nvarchar(max), but during the saving operation the data gets truncated.

The trouble arise due to some small modifications to the SqlClinetDriver that were made to optimize the reuse of the query plan Sql Server will generate, you can enable the same feature in NHibernate 2.1.x setting the ‘prepare_sql’ configuration option to true.

Basically the problem is in the way the parameters of the SqlCommand are generated: without using prepare_sql=true the length of the string parameter is computed on the the data you pass in (ie: ‘aaa’ is represented by string(3)) and this is limiting the database engine capability of reusing query plans (more info in a link later on); but you can map an nvarchar(max) column like this:

<property name="StringHugeLength" column="StringHugeLength" type="string" length="10000" />

and everything works as expected.

The following commented piece of mapping sums up the behavior I’ve noticed when using prepare_sql=true:

<!-- this generates an nvarchar(255), data is saved as string(4000)-->
<property name="StringDefault" column="StringDefault" type="string" />
<!-- this generates an nvarchar(50), data is saved as string(50) -->
<property name="StringFixedLength" column="StringFixedLength" type="string" length="50" />
<!-- this generates an nvarchar(max), data is truncated at the length specified: string(10000) -->
<property name="StringHugeLength" column="StringHugeLength" type="string" length="10000" />
<!-- this generates an nvarchar(max), data is truncated at the default string length: string(4000), 
    the sql-type does not participate in the runtime parameter generation for the query -->
<property name="StringSqlType" type="string">
	<!-- if you specify the length explictly it will truncate at the specified limit instead of the default -->
	<column name="StringSqlType" sql-type="nvarchar(max)" />
</property>
<!-- this generates an nvarchar(255), reading and writing work for legacy nvarchar(max) fields -->
<property name="Blob" column="Blob" type="StringClob" />
<!-- this generates an nvarchar(max), data is truncated at the length specified: string(10000) -->
<property name="BlobLength" column="BlobLength" type="StringClob" length="10000" />
<!-- this mapping works! for generation, reading and writing -->
<property name="BlobSqlType" type="StringClob" >
	<column name="BlobSqlType" sql-type="nvarchar(max)" />
</property>

I have to admit I didn’t knew/used this feature in the past and I faced the problem only recently...here’s a good post from Andrei Volkov that sums up the whole story and point to the problem:

http://zvolkov.com/blog/post/2009/10/28/NHibernate-parameter-sizes-controversy.aspx

Even using those suggestions I’m still not totally satisfied of the solution; moreover the only mapping that works correctly (the last one in the previous example) does not satisfy me much, because it introduces a dependency to the specific database types.

From my point of view I would like to use the following mapping syntax to deal with ‘nvarchar(max)’ columns:

<property name="Blob" column="Blob" type="StringClob" />

This one should emit an nvarchar(max), which I consider my default type when dealing with long text, when using ddl and SchemaExport() and should be able to load and save data to that field without truncation. Tto map a varchar(max) you can use the <column> tag inside property and specify a sql-type explictly.

I have made my own modifications to the Sql Driver and Dialect to have NHibernate work the way I think it should (I really like its highly customizable environment)...more on this subject in some next posts (these changes should not have any impact on other dialects that do not have the same Sql Server problems, and you should be able to reuse your ‘new’ mapping without modifying them).

I have no clue if the NHibernate team will revert the way ‘prepare_sql’ works in the current version or if they modify things again. But in the meanwhile I’m happy with my current solution which has minimum impact over my mappings.

Happy NHibernating!


Nhibernate, Nvarchar(max), Prepare_sql

0 comments

Tale of an ‘asp:Content control that do not correspond with asp:ContentPlaceHolder’ Master Page Error

Print Content | More

If, like me, you are so unlucky to encounter the infamous:

‘The page has one or more <asp:Content> control that do not correspond with <asp:ContentPlaceHolder> control in master page.’

contentplaceholdererror

Figure 1 - The designer showing the master page error

error message when dealing with Asp.Net pages that actually use a master page, and you are unable to use the designer to drop and configure controls (raise your hand if you still use the designer instead of doing everything by hand!) you can do the following thing:

  • go for the obvious resolution and check for the names of the content place holders.

If the problem still persists and you’re sure your content controls and content place holders are correctly mapped to each others (you can let the wizard generate the page for you) and moreover everything actually works at runtime (even the designer for the master page works!), the error is surely somewhere in the HTML markup and this very informative error message won’t help you find it out.

I started looking at the markup and everything seemed ok to me, even a friend of mine confirmed me the markup was ok...until I started looking very carefully and spotted the problem: this page was derived from an old project and maintained by lot of persons...so it ended having some markup and HUGE portions of the page commented out, the master page had some code like this:

...
<div id="divCentralBlock">
    <div id="header_down">
    </div>
    <div class="line">
    </div>
    <%-- Breadcrumb --%>
    <%--   <div id="SiteMap_Contenitore">
            <asp:SiteMapPath ID="MainSiteMapPath" runat="server" PathSeparator=" : " CssClass="fnsize76pr"
                CurrentNodeStyle-CssClass="currentNode" NodeStyle-CssClass="node" SiteMapProvider="SiteMapPath"
                meta:resourcekey="MainSiteMapPathResource1" Visible="false" Enabled="false">
                <CurrentNodeStyle CssClass="currentNode" />
                <NodeStyle CssClass="node" />
            </asp:SiteMapPath>
            <asp:SiteMapDataSource ID="SiteMapDataSource1" runat="server" /> 
            <%-- <cc1:scriptmanager id="smAtlas" runat="server"></cc1:scriptmanager> --%>
    <%-- </div>
	... a lot more commented code wiped out
	--%>
    <div id="contentBox">
        <div id="contentTitle">
            <asp:ContentPlaceHolder ID="TitlePlaceHolder" runat="server">
            </asp:ContentPlaceHolder>
        </div>
        <div id="contentBody">
            <div>
                <asp:ContentPlaceHolder ID="MainPlaceHolder" runat="server">
                </asp:ContentPlaceHolder>
            </div>
        </div>
    </div>
</div>
...

As you can see lines 7 to 18 contain a lot of comments, the visual studio html syntax verifier does not complain about everything when looking at this code, but if you look carefully at the end of line 14 you can see the missing of a close comment tag ( --%> ), neither me nor my friend saw this problem because at our first look WE SKIPPED THE COMMENTED COMPLETELY, giving it for granted that they couldn’t cause any trouble (what a mistake!).

This lack was causing a lot of troubles to the designer that wasn’t able to parse the master page correctly. Closing the comment in the correct way solved the problem and we were able to use the master page in the designer again; the lesson is: always respect the syntax of the language you’re using! (and always look at everything when things don’t work as expected).


Asp net, Master page, Error, Content, Contentplaceholder

0 comments

Spot Hidden Exceptions using IntelliTrace

Print Content | More

Recently I’ve been able to replace my Visual Studio 2010 Professional with Visual Studio 2010 Ultimate and the first feature I wanted to try was IntelliTrace, to see how it could help me debug and improve the quality of my software.

After giving a look at what it’s capable the first and simple way I used it was to look at all the exception messages raised by my applications; just looking at them I was able to discover and eliminate some ‘hidden’ problem which were simply eaten up the framework (in case of bindings errors for example) or by a wrong exception handling strategy that was implemented in code.

To enable IntelliTrace go to Tools -> Options and look for the IntelliTrace section there, you will see the following dialog:

IntellitraceExceptions1

Figure 1 - IntelliTrace Options

If you want to enable detailed information for the call tree enable the second option, but it will slow down your debugging experience a lot, so do it if you really need those information; I will also suggest you to check the advanced tab and increase the default size of the maximum amount of disk space for each recording.

Run the application with the debugger attached and when you break it you can analyze the IntelliTrace log:

IntellitraceExceptions2

Figure 2 - IntelliTrace in action, full event log

Clicking on an entry will open up the corresponding source code file and will highlight the line of interest. The default view is quite messy, because it contains logs for each type of event you asked for in the IntelliTrace Options, you can filter it up easily and show only what you want to focus on (the exceptions at the moment).

IntellitraceExceptions3

Figure 3 - Filtering the data

Wow! I never expected that my application raised so many exceptions under the hood, so I started to look at them to see if I could fix them. Inspecting this log you can see all the exceptions that ‘flows’ out of your direct control. Some of them are particularly nasty, like the ones originated from the binding system, take the following one as example:

IntellitraceExceptions4

Figure 4 - Analyzing a single exception.

Here we have a binding to a ComboBox which should hold a list of names of nations, using the software everything was working well: I could see the nations and select them and the value was correctly saved. But internally we had a System.ArgumentException that was throw and eaten up by the binding framework...why? looking at the code it’s quite clear: we are making a binding with a list of strings but we’ve also specified DisplayName and DisplayValue (maybe this control was previously in binding with something else) and this is a mistake we ignored because it has no evident effect.

The binding framework just ignores those two values...but nonetheless an avoidable exception has been thrown and captured; the solution in this case is easy: remove the assignments to DisplayMember and ValueMember.

Using IntelliTrace in this way and carefully looking at the logs I was able to remove a lot of those hidden exceptions (especially when dealing with bindings) with an overall improvement of the quality (and performance) of the application.


Visual studio, IntelliTrace, Exception

0 comments

A Windows Live Essentials Suite Beta is out...how to workaround the ‘no internet connection’ setup problem

Print Content | More

A new version of the Windows Live Essential Suite was released on August the 17th, you can get it here (at your own risk!); if you want to try it out you can have a very bad surprise:

after having chosen the applications you want to install, at the end of the setup the whole procedure might fail with a very informative ‘No internet connection’ error message.

You can have a look at the real problem looking at the Windows Live setup log (located in: C:\ProgramData\Microsoft\WLSetup\Logs), which is usually a missing dependency or something similar.

In my case it seemed the installer was trying to download a language pack (Italian, in my case) which is not currently available and that caused the setup process to stop.

Switching all the locale information of my system to the ‘United States’ language didn’t solved the problem.

The trick to overcome this problem is simple:

- uninstall all your current Windows Live Essentials

- go to: “C:\Program Files\Common Files\Windows Live\.cache” and clean it up, this is the folder where the setup files will be downloaded

- run the setup again and when it fails hit ‘Cancel’

- go to the previous folder and rerun all the setup modules you find inside each sub-directory.

Everything should work now! (At least it did for my installation).

I’m still not able to log in MSN Messenger due the the nasty 80040154 error, it seems no-one is able to discover what’s causing it, but now I can use and test the new Live Writer again (and I’m happy with that).


Windows Live Essentials

0 comments

NHibernate 3 - Extending the Linq Provider to fix some System.NotSupportedException

Print Content | More

With the release of the new version NHibernate (3.0 alpha1), I’ve decided to give it a try and branch my current solution to switch to this new version and see hoe it goes.

I was especially interested in the new Linq support, cause I’ve decided to use it as the basis for my data access strategies.

After the necessary reference changes I run all my test suit...and I had bad news from the Linq provider in the form of some System.NotSupportedException like this one:

“UnitTest.Test03_Linq.QueryWithEqualsGuid:
System.NotSupportedException : Boolean Equals(System.Guid)”

Being NHibernate an open source project, instead of bothering the guys responsible for the project, my first approach is always to look at the code; so I downloaded the trunk and started looking around at the Linq provider. Watching how Equals() methods are handled by the parser it comes out fast that only the specific version to deal with strings is currently supported [bool Equals(string)], all other types have to rely on the ==operator.

But in my code I had a lot of filters based on Equals() call for various object types (int, guid and so on...) and I didn’t wanted to touch that code especially considering that with the previous Linq provider everything was working well.

However the solution is easy, just extend the default EqualsGenerator adding the support for the missing methods; but I didn’t wanted to compile a specific ‘patched’ version of NHibernate and this post from Fabio Maulo confirmed me you can easily extend the Linq provider. Great! That’s was exactly what I was looking for!

I started working on it and I had my second surprise Open-mouthed smile. The Linq provider was subhect of a heavy refactoring activity to provide better extensibility from the version you have in 3.0 alpha1. Using reflector and looking around in the binaries it comes out that to extend that provider you have to register your extension methods calling the methods of the NHibernate.Linq.Functions.FunctionRegistry class. But in all honesty I think that the way it works in alpha2 is way more elegant and it follows better the standard approach NHibernate have when it comes to configure its components.

So if you have to extend the Linq provider forget of alpha1 and compile your own version of NHibernate getting it from the Trunk.

Back to the job now: following Fabio’s instructions (and looking at the code) I came out with these classes:

public class ExtendedEqualsGenerator : BaseHqlGeneratorForMethod
{
	public ExtendedEqualsGenerator()
	{
		// the methods call are used only to get info about the signature, the actual parameter is just ignored
		SupportedMethods = new[] { 
			ReflectionHelper.GetMethodDefinition<Byte>(x => x.Equals((Byte)0)),
			ReflectionHelper.GetMethodDefinition<SByte>(x => x.Equals((SByte)0)),
			ReflectionHelper.GetMethodDefinition<Int16>(x => x.Equals((Int16)0)),
			ReflectionHelper.GetMethodDefinition<Int32>(x => x.Equals((Int32)0)),
			ReflectionHelper.GetMethodDefinition<Int64>(x => x.Equals((Int64)0)),
			ReflectionHelper.GetMethodDefinition<UInt16>(x => x.Equals((UInt16)0)),
			ReflectionHelper.GetMethodDefinition<UInt32>(x => x.Equals((UInt32)0)),
			ReflectionHelper.GetMethodDefinition<UInt64>(x => x.Equals((UInt64)0)),
			ReflectionHelper.GetMethodDefinition<Single>(x => x.Equals((Single)0)),
			ReflectionHelper.GetMethodDefinition<Double>(x => x.Equals((Double)0)),
			ReflectionHelper.GetMethodDefinition<Boolean>(x => x.Equals(true)),
			ReflectionHelper.GetMethodDefinition<Char>(x => x.Equals((Char)0)),
			ReflectionHelper.GetMethodDefinition<Decimal>(x => x.Equals((Decimal)0)),
			ReflectionHelper.GetMethodDefinition<Guid>(x => x.Equals(Guid.Empty)),
		};
	}

	public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, ReadOnlyCollection<Expression> arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
	{
		return treeBuilder.Equality(
				visitor.Visit(targetObject).AsExpression(),
				visitor.Visit(arguments[0]).AsExpression());
	}
}

public class ExtendedLinqtoHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry
{
	public ExtendedLinqtoHqlGeneratorsRegistry()
	{
		this.Merge(new ExtendedEqualsGenerator());
	}
}

After registering them (actually it can be done only in code) using:

configuration.SetProperties("linqtohql.generatorsregistry", "Nhibernate.Extensions.ExtendedLinqtoHqlGeneratorsRegistry, Nhibernate.Extensions");

or

configuration.LinqToHqlGeneratorsRegistry<ExtendedLinqtoHqlGeneratorsRegistry>();

My tests passed again and I didn’t had to touch a single line of code. This is simply amazing! (but I think the full support for all the equals methods should have been added to the core anyway).


Nhibernate, Linq, Extensibility

0 comments

Playing with Profilers - always take a look at your most called functions!

Print Content | More

...even if they don’t seem to consume too much time. I have finally found some ‘time slots’ to assign to code profiling activities, so I decided to take advantage of the dotTrace License that was kindly awarded to me by JetBrains (thanks again to JetBrains for supporting DotNetMarche and our workshops). I’m a newbie when it comes to profiling so I’ll just share my experiences and I’ll show how everyone can take advantage of these tools to improve the performances and sometimes the quality of your code.

In this post I’ll show you a very basic usage of the dotTrace profiler, but it helped me a lot in finding some bad spots in my code.

Let’s kick off by starting the profiler, I’m lazy so I’ll just use the Visual Studio integration and use the menu ‘dotTrace -> Profile the Startup Project’; it will rebuild the project and show you the following option dialog box:

dotTraceStartUp

Figure 1 - dotTrace startup dialog.

I’m crazy and I want very high precision and accuracy, so I set the profiling type to ‘Line-By-Line’ and asked to start the profiling immediately. Note that setting this profiling type will make your application painfully slow but it will give you the most accurate results. Play with your application a bit using the forms and the functions you want to profile, when you are ready take a snapshot of the application using the dotTrace control window.

This will open up the profiler main window and you can use that to have different views of the data it gathered.

For a fist shot analysis I’m just interested in looking at the most called functions, dotTrace can show me a plain list of all the function calls which I can sort and group by class name or namespace. You can think of this particular view as ‘having a look at the most active functions, classes or namespace in your application’. Here’s what I got during my first attempt, I like to have the result grouped by namespace and sorted by the number of calls:

dotTraceNamespaceView

Figure 2 - dotTrace Plain List view grouped by namespace.

As you can see the first places of the list are all taken by NHibernate and Castle functions, at this point I’m not interested in those...but you can also see a rgmComponents namespace that ‘is making’ a lot of calls (even if the time consumed is small); I expanded it too see the list of functions in detail and you can see that a single color conversion function was called loads of times (18k calls!) in a very shot running time of the application.

This application shouldn’t be so graphically intensive...so it rang a warning bell to me. This very high amount of calls must be an incorrect use of the function itself or some side effect that makes it to be called when it should not be needed; what you can do is right-click -> ‘open in New Tab’ in the most called function to dig into it even more, here you can have different views of the related data. One of the most interesting is the ‘Back Tracking’ view, which shows you who is calling the function you are watching (actually that’s the info I’m looking forward to have):

dotTraceDetail

Figure 3 - Back Trace of a function.

Looking at this data you can see that all the 18k+ calls of this function were originated by the setter of the property ‘SecondHeaderColor’ of the XpTaskBox control: it’s now time to take a look at the control’s code because it really smells.

I will skip all the code analysis here, but looking at the code it was clear that that the control was generating cached copies of an icon changing the colors to represent different states (enabled, inactive, etc...) but it was generating them even if the icon wasn’t actually rendered (it was an expand/collapse icon) nor used. Using the profiler in this way helped me to find out some poorly designed code, I just changed the implementation to use lazy initialization for those images and compute them only if really needed...this allowed me to cut down the number of call to this function from 18k times to 5k times only...which in turn lead to a global faster rendering of the whole UI of the application and better performances in the long run (no more computations if not needed).

So a profiler not only helps you in finding weak spots in algorithms showing you the most time consuming functions, but can also be used to find weak spots in your code design.


Visual studio, JetBrains, DotTrace, Profiling

3 comments

How to update your TFS Workspace after CodePlex upgrade to TFS2010

Print Content | More

Recently CodePlex updated their TFS to the 2010 version, during the update your reference TFS server may have changed name (like it happened for Dexter, our original TFS server was tfs05.codeplex.com and now it is tfs.codeplex.com).

To switch to the new server all I did was, open your solution in Visual Studio, it will open in offline mode due to the fact that the old TFS is no more accessible.

After that go to: ‘File --> Source Control --> Workspaces...’

You will be prompted with a dialog that asks for a new TFS address and new access credentials, take those from your CodePlex project website.

Once connected a new Dialog Box will open allowing you to select the project to which link your solution among all the Team Project Collections available to your account.

TeamProjectCollections

Select Connect and check if your workspace has been associated correctly.

To make the change fully effective, close and reopen your solution, you will be asked if you want to ‘Go Online’.

That’s all you need to do in order for having your TFS integration work again after the CodePlex upgrade.


Visual studio, Team Foundation Server

0 comments

NHibernate - Eager Fetch and Pagination in one query

Print Content | More

Let’s consider these mappings:

<class name="Item" table="Item">
	<id name="Id" column="Id" type="int">
		<generator class="native" />
	</id>
	<property name="Name" column="Name" type="string" />
	<property name="Sort" column="Sort" type="int" />
	<bag name="Tags" inverse="true" lazy="true">
		<key column="ItemId" />
		<one-to-many class="Tag"/>
	</bag>
</class>

<class name="Tag" table="Tag">
	<id name="Id" column="Id" type="int">
		<generator class="native" />
	</id>
	<property name="Name" column="Name" type="string" not-null="true" />
	<many-to-one name="Item" column="ItemId" class="Item" not-null="true" />
</class>

 

Trying to paginate a query that uses eager fetching to retrieve the data in the ‘traditional’ way, that is using something similar to this piece of code:

ICriteria myCriteria = session.CreateCriteria<Item>("itm");
myCriteria.AddOrder(new Order("Sort", true))
	.SetFirstResult((pageIndex - 1)*pageSize)
	.SetMaxResults(pageSize)
	.SetResultTransformer(Transformers.DistinctRootEntity)
	.SetFetchMode("Tags", FetchMode.Eager);

 

Will simply FAIL, because eager fetching retrieves more data rows from the database due to the joins it performs, thus the ‘SetFirstResult()’ call will simply cut out some of your data.

One way to resolve the problem is to use a DetachedCriteria to actually get a projection of all the Ids of the items you want to paginate (applying any filtering and sorting you desire at this step) and later on use the projection to perform the real query that retrieves the data using eager fetching:

private static IList<Item> GetPagedData(int pageIndex, int pageSize)
{
	IList<Item> result;
	using (ISession session = NHelper.OpenSession())
	using (ITransaction tx = session.BeginTransaction())
	{
		// get the list of IDs corresponding to the page od data we wanna get
		DetachedCriteria detached = DetachedCriteria.For<Item>("itm2");
		detached.AddOrder(new Order("Sort", true))
			.SetFirstResult((pageIndex - 1)*pageSize)
			.SetMaxResults(pageSize)
			.SetProjection(Projections.Property("Id"));
		// get the real data
		ICriteria myCriteria = session.CreateCriteria<Item>("itm");

		myCriteria
			.Add(Subqueries.PropertyIn("Id", detached))
			.SetResultTransformer(Transformers.DistinctRootEntity)
			.SetFetchMode("Tags", FetchMode.Eager);
			
		result = myCriteria.List<Item>();

		tx.Commit();
	}
	return result;
}

 

If you look at the logs generated by NHibernate you will see that this code will result in a single query sent to your database engine, having called this function asking for page index 2 with a page size of 2 I obtain the following query:

SELECT this_.Id as Id1_1_, this_.Name as Name1_1_, this_.Sort as Sort1_1_, tags2_.ItemId as ItemId3_, tags2_.Id as Id3_, tags2_.Id as Id0_0_, tags2_.Name as Name0_0_, tags2_.ItemId as ItemId0_0_ FROM dbo.Item this_ left outer join dbo.Tag tags2_ on this_.Id=tags2_.ItemId WHERE this_.Id in (SELECT TOP 2 y0_ FROM (SELECT this_0_.Id as y0_, ROW_NUMBER() OVER(ORDER BY this_0_.Sort) as __hibernate_sort_row FROM dbo.Item this_0_) as query WHERE query.__hibernate_sort_row > 2 ORDER BY query.__hibernate_sort_row)

This technique helped me to reduce the number of queries made by the application a lot.


Eager, Nhibernate, Pagination

0 comments

Tomorrow’s DotNetMarche Workshop Live Streaming

Print Content | More

As anticipated in my previous post, if you cannot be there to attend the workshop directly (and you should be Open-mouthed smile), thanks to the guys at Ce.S.M.I. we will be able to offer you a live streaming of the event (crossing fingers for everything to work as expected...), here’s the link to attend the workshop online:

mms://mmedia.univpm.it/dotnetmarche.org

Since we are a ‘local’ User Group the workshop and the material will be presented in Italian; more info and the complete agenda on our official DotNetMarche website.

Thank you again for the support and opportunity you give us to share our passion for developing software even more.


Dotnetmarche, Workshop

0 comments