Partial Object Queries With NHibernate

time to read 2 min | 310 words

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.