In NHibernate, if an entity is lazy-loaded by one query, and then requested to be eager-loaded by a second query in the same session, the returned type in the second query is always a proxy (I believe a cached proxy from the first query). This can be demonstrated in this highly simplified and British example:
public class CarClassMap : ClassMap<Car>
{
public CarClassMap()
{
this.Id(x => x.Id);
this.Map(x => x.Model);
// note: lazy load by default
this.References(x => x.Colour).Column("ColourId").LazyLoad();
}
}
public class ColourClassMap : ClassMap<Colour>
{
public ColourClassMap()
{
this.Id(x => x.Id);
this.Map(x => x.Description);
}
}
public static void Main()
{
using (var session = CreateSessionFactory().OpenSession())
{
// note: both cars are the same colour in the database
var jaguar = session.QueryOver<Car>()
.Where(x => x.Id == 1)
.FutureValue()
.Value;
Console.WriteLine("Jaguar colour type=" + jaguar.Colour.GetType());
var aston =
session.QueryOver<Car>()
.Fetch(x => x.Colour).Eager //note: eager load
.Where(x => x.Id == 2)
.FutureValue()
.Value;
Console.WriteLine("Aston Martin colour type=" + aston.Colour.GetType());
}
Console.Read();
}
The output from this program is:
Jaguar colour type=ColourProxy
Aston Martin colour type=ColourProxy
Both 'Colour' properties are proxies, despite the second query requesting an eager load. However when running just the eager-load query:
public static void Main()
{
using (var session = CreateSessionFactory().OpenSession())
{
var aston =
session.QueryOver<Car>()
.Fetch(x => x.Colour).Eager
.Where(x => x.Id == 2)
.FutureValue()
.Value;
Console.WriteLine("Aston Martin colour type=" + aston.Colour.GetType());
}
Console.Read();
}
The output is:
Aston Martin colour type=TestApp.Colour
The concrete, underlying type.
In our real system, the returned object is passed to a mapping layer which performs some complex logic. This discrepancy is causing us issues because the property is typed differently depending on which queries have been previously issued in the Session.
Basically the question is how can we avoid a request for an eager-load resulting in a proxy, and rather force it to the concrete type? We know we can manually 'un-proxy' objects using NHibernate utilities but we would rather not have to do that every time this entity is queried. If possible we'd prefer a way to do it in the class map. Or is there an altogether better solution? Thanks.
NHibernate by default guarantees uniqueness of instances a session returns for a same entity. That is why your eager load returns the previously loaded lazy proxy if there was one in the same session for the entity (which by the way should then have been fully initialized).
You may have quite a hard time trying to dodge this, depending on how your application works.
You may Clear
the session before your eager load for avoiding that, but this will cancel all pending changes and render any unloaded proxy unusable while any previously loaded entities would be detached from session.
You may instead Evict
only your entity if you have a reference to it beforehand, but it does not look to be the case when reading your question.
I would rather adapt the mapping layer for it to support getting a proxy or the base entity class. This question is about that and has a bunch of interesting answers.
In your case, Diego Mijelshon answer's linked blog could suits you well for allowing your mapping layer to always get the concrete class.
It consists of adding a property to your entity class which will yield you the concrete instance whether you have already it or you have a proxy.
public virtual object Actual { get { return this; } }
As he warns, this is a hack. The concrete instance obtained by this method should not be used with the NHibernate session afterward.