Ayende @ Rahien

It's a girl

Partial Object Queries With NHibernate

Aaron still wants partial object queries, so I set up to build them using NHibernate. Here is the implementation, notice that this query will result in a list of Blog instances, but the select will only include their titles & subtitles.

using (ISession session = sessionFactory.OpenSession())
{
	TupleToPropertyResultTransformer transformer = 
		new TupleToPropertyResultTransformer(typeof(Blog),"Title", "Subtitle");
	IList list = session.CreateQuery("select b.Title, b.Subtitle from Blog b")
		.SetResultTransformer(transformer)
		.List();
	foreach (Blog b in list)
	{
		System.Console.WriteLine("Blog: {0} - {1}", b.Title, b.Subtitle);
	}
}

But where does TupleToPropertyResultTransformer comes from, well, that is where the magic comes in, here is my implementation for it:

public class TupleToPropertyResultTransformer : IResultTransformer
{
	private Type result;
	private PropertyInfo[] properties;

	public TupleToPropertyResultTransformer(Type result, params string[] names)
	{
		this.result = result;
		List<PropertyInfo> props = new List<PropertyInfo>();
		foreach (string name in names)
		{
			props.Add(result.GetProperty(name));	
		}
		properties = props.ToArray();
	}

	public object TransformTuple(object[] tuple, string[] aliases)
	{
		object instance = Activator.CreateInstance(result);
		for (int i = 0; i < tuple.Length; i++)
		{
			properties[i].SetValue(instance, tuple[i], null);
		}
		return instance;
	}

	public IList TransformList(IList collection)
	{
		return collection;
	}
}

This isn't the most optimized version that you can think of, but it does the job.

I want to make it clear, however, that I feel that doing stuff like this is not something that I would consider to be a best practice. Quite the opposite, frankly. What we have here is an object in a state that it was never intended to be, with only part of its fields filled, and certainly not based on any logic. I would much rather see a DTO class take its place, because that has a clear responsibility in the application, reusing your entities as dumb data container is not something that I would recommend.

Comments

jmajaranta
08/27/2007 05:57 AM by
jmajaranta

Wouldn't the AliasToBeanResultTransformer do exactly the same thing?

I was trying to get NHibernate's projections to return hierarchial

DTO's, that is a DTO child objects and setting the child object's properties, and also collections, so you could get NHibernate to fill something like MyDTO.MyChildDTO.Name and MyDTO.DTOChildren[0].Name (and of course MyDTO.MyChildDTO.MyChildDTO2.Name and MyDTO.DTOChildren[0].MyChildDTO.Name).

I couldn't find any other way to do this than writing a custom IResultTransformer.

Do you know if NHibernate

would support this out-of-the-box or is a custom IResultTransformer the way to go ?

Colin Jack
08/27/2007 08:34 AM by
Colin Jack

Interesting to know that its possible but as you say its probably something you want to avoid.

Ayende Rahien
08/27/2007 04:40 PM by
Ayende Rahien

The AliasToBeanResultTransformer is built to be used with a criteria.

I would suggest building a customer result transformer for that, it is very simple,and you can have it do it your way

Ben Scheirman
08/27/2007 06:19 PM by
Ben Scheirman

While the code is cool, I agree with you on the point that this leads to fuzzy-looking objects.

Picture for example you have an .Equals() implementation that checks those 2 properties + the author's name. The author's name isn't loaded so it needs to trigger a refresh.

So now if we use a semi-harmless method like .Contains() we will likely have introduced one of the leakiest abstractions that we could possibly accomplish. Picture this...

if(blogs.Contains(partialBlogInstance))

{

//do something

}

This code will now either have the strange side effect of going to the database --or -- throwing an exception because we've lost session at this point.

I can't see why someone would really want to do this. Write a DTO for things like dropdowns and pull those out of your tuple arrays.

jmajaranta
08/28/2007 04:05 AM by
jmajaranta

Thank you for your time Ayende.

Yeah, the transformer was simple to build, except for the collections.

Now the transformer works by transforming tuples first to a hiearchy

where the DTO's collections contain only one item, and then consolidates

the collections in the TransformList method to a distinct root DTO which has to provide some business ID to group the collections. A dirty solution was to use GetHashCode() for now...

I didn't read your code careful enough to realize you were using the transformer with HQL queries.

Nice ;) I was under the impression the transformers wouldn't work

with HQL queries (although supported) because I couldn't get the built-in transformers to work (mainly the AliasToBeanResultTransformer). Now I know why I ;)

freeblog
08/29/2007 08:29 AM by
freeblog

Register your own WordPress blog here!

Fabio Maulo
09/18/2007 11:40 PM by
Fabio Maulo

A year ago i made something similar in a prj.

I port it to uNhAddIns naming it PositionalToBeanResultTransformer.

Apparently is not a best practices but, if more than one developer need it, it is useful.

;)

Bye.

Fabio.

Comments have been closed on this topic.