Ayende @ Rahien

Hi!
My name is Oren Eini
Founder of Hibernating Rhinos LTD and RavenDB.
You can reach me by phone or email:

ayende@ayende.com

+972 52-548-6969

, @ Q c

Posts: 5,971 | Comments: 44,508

filter by tags archive

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

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

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

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

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

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

Register your own WordPress blog here!

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.

Comment preview

Comments have been closed on this topic.

FUTURE POSTS

  1. Paying the rent online - 3 days from now

There are posts all the way to Aug 03, 2015

RECENT SERIES

  1. Production postmortem (5):
    29 Jul 2015 - The evil licensing code
  2. Career planning (6):
    24 Jul 2015 - The immortal choices aren't
  3. API Design (7):
    20 Jul 2015 - We’ll let the users sort it out
  4. What is new in RavenDB 3.5 (3):
    15 Jul 2015 - Exploring data in the dark
  5. The RavenDB Comic Strip (3):
    28 May 2015 - Part III – High availability & sleeping soundly
View all series

Syndication

Main feed Feed Stats
Comments feed   Comments Feed Stats