Make lazy object load real instance

I have a proxy for a lazy object that was created in a session by loading a child. Subsequent fetching on the parent returns only NH proxies. I need an actual instance for type checking (the object has joined subclasses). I have to miss something, but I cannot find a way to do this. Session.Refresh (proxy) does not seem to help, and no taste of the HQL I tried.

Can anyone help?

+6
lazy-evaluation nhibernate
source share
4 answers

To force proxies to get from the database, you can use the NHibernateUtil.Initialize(proxy) method or access the proxy method / property.

 var foo = session.Get<Foo>(id); NHibernateUtil.Initialize(foo.Bar); 

To check if an object is initialized or not, you can use the NHibernateUtil.IsInitialized(proxy) method.

Update:

To remove an object from the session cache, use the Session.Evict(obj) method.

 session.Evict(myEntity); 

Information on Evict and other session cache management techniques can be found in chapter 14.5 of the NHibernate documentation.

+11
source share

In my opinion, instead of solving this problem, you should rather rethink your design. You are absolutely sure that in this situation you cannot use polymorphism - either directly force the entity to be responsible for the work you are trying to do, or use a visitor template. I came across this problem several times and always decided to change the design - this led to a clearer code. I suggest you do the same if you are not sure that relying on type is the best solution.

Problem

To have an example with at least some similarities with the real world, suppose you have the following entities:

 public abstract class Operation { public virtual DateTime PerformedOn { get; set; } public virtual double Ammount { get; set; } } public class OutgoingTransfer : Operation { public virtual string TargetAccount { get; set; } } public class AtmWithdrawal : Operation { public virtual string AtmAddress { get; set; } } 

This, of course, will be a small part of a much larger model. And now you have a problem: for each specific type of operation, there is another way to display it:

 private static void PrintOperation(Operation operation) { Console.WriteLine("{0} - {1}", operation.PerformedOn, operation.Ammount); } private static void PrintOperation(OutgoingTransfer operation) { Console.WriteLine("{0}: {1}, target account: {2}", operation.PerformedOn, operation.Ammount, operation.TargetAccount); } private static void PrintOperation(AtmWithdrawal operation) { Console.WriteLine("{0}: {1}, atm address: {2}", operation.PerformedOn, operation.Ammount, operation.AtmAddress); } 

Simple, overloaded methods will work in the simple case:

 var transfer = new OutgoingTransfer { Ammount = -1000, PerformedOn = DateTime.Now.Date, TargetAccount = "123123123" }; var withdrawal = new AtmWithdrawal { Ammount = -1000, PerformedOn = DateTime.Now.Date, AtmAddress = "Some address" }; // works as intended PrintOperation(transfer); PrintOperation(withdrawal); 

Unfortunately, overloaded methods are bound at compile time, so as soon as you enter an array / list / regardless of operations, only the general (operational operation) overload is called.

 Operation[] operations = { transfer, withdrawal }; foreach (var operation in operations) { PrintOperation(operation); } 

There are two solutions to this problem, and both have flaws. You can introduce an abstract / virtual method in an operation to print information into a selected stream. But this will mix UI issues with your model, so this is not acceptable for you (I will show you how you can improve this solution to meet your expectations in an instant).

You can also create many ifs in the form:

 if(operation is (ConcreteType)) PrintOperation((ConcreteType)operation); 

This solution is ugly and error prone. Each time you add / modify / delete the type of operation, you need to go through all the places where you used these hacks and change them. And if you skip one place, you can probably only catch this runtime - no strict compile-time checks for some errors (for example, skip one subtype).

In addition, this solution will not work as soon as you submit any proxy server.

How the proxy server works

The code below is a VERY simple proxy (in this implementation, it is similar to the decorator pattern), but these patterns generally do not match. To distinguish between these two patterns, one more code is required.)

 public class OperationProxy : Operation { private readonly Operation m_innerOperation; public OperationProxy(Operation innerOperation) { if (innerOperation == null) throw new ArgumentNullException("innerOperation"); m_innerOperation = innerOperation; } public override double Ammount { get { return m_innerOperation.Ammount; } set { m_innerOperation.Ammount = value; } } public override DateTime PerformedOn { get { return m_innerOperation.PerformedOn; } set { m_innerOperation.PerformedOn = value; } } } 

As you can see, there is only one proxy class for the entire hierarchy. What for? Because you must write your code in such a way that it does not depend on a particular type - only subject to abstraction. This proxy server could delay loading the object on time - maybe you will not use it at all? Maybe you will use only 2 out of 1000 objects? Why download them all?

As such, NHibernate uses a proxy server as above (much more complicated though) to delay the loading of the object. It can create 1 proxy server for each subtype, but it will destroy the whole lazy load target. If you look closely at how NHibernate stores subclasses, you will see that to determine what type of entity you must load it. Thus, it is not possible to have specific proxies - you can only have the most abstract OperationProxy operation.

Although the solution with ifs was ugly - it was a solution. Now that you have submitted proxies to your problem, it no longer works. So this leaves us with a polymorphic method, which is unacceptable due to the bias of the UI responsibility with your model. Let me fix it.

Dependency Inversion and Visitor Pattern

First, let's see what the solution with virtual methods looks like (only added code):

 public abstract class Operation { public abstract void PrintInformation(); } public class OutgoingTransfer : Operation { public override void PrintInformation() { Console.WriteLine("{0}: {1}, target account: {2}", PerformedOn, Ammount, TargetAccount); } } public class AtmWithdrawal : Operation { public override void PrintInformation() { Console.WriteLine("{0}: {1}, atm address: {2}", PerformedOn, Ammount, AtmAddress); } } public class OperationProxy : Operation { public override void PrintInformation() { m_innerOperation.PrintInformation(); } } 

And now, when you call:

 Operation[] operations = { transfer, withdrawal, proxy }; foreach (var operation in operations) { operation.PrintInformation(); } 

everything works like a charm.

To remove this UI dependency in the model, create an interface:

 public interface IOperationVisitor { void Visit(AtmWithdrawal operation); void Visit(OutgoingTransfer operation); } 

Change the model that will depend on this interface:

And now create an implementation - ConsoleOutputOperationVisitor (I removed the PrintInformation methods):

 public abstract class Operation { public abstract void Accept(IOperationVisitor visitor); } public class OutgoingTransfer : Operation { public override void Accept(IOperationVisitor visitor) { visitor.Visit(this); } } public class AtmWithdrawal : Operation { public override void Accept(IOperationVisitor visitor) { visitor.Visit(this); } } public class OperationProxy : Operation { public override void Accept(IOperationVisitor visitor) { m_innerOperation.Accept(visitor); } } 

What's going on here? When you call Accept on operation and pass in the visitor, the accept implementation will be called when the Visit method is overloaded (the compiler can determine the type of "this"). Thus, you combine the "power" of virtual methods and overloads to get the appropriate method. As you can see - now there is a link to the UI, the model depends only on the interface, which can be included in the model layer.

So, to get this working, the implementation of the interface:

  public class ConsoleOutputOperationVisitor : IOperationVisitor { #region IOperationVisitor Members public void Visit(AtmWithdrawal operation) { Console.WriteLine("{0}: {1}, atm address: {2}", operation.PerformedOn, operation.Ammount, operation.AtmAddress); } public void Visit(OutgoingTransfer operation) { Console.WriteLine("{0}: {1}, target account: {2}", operation.PerformedOn, operation.Ammount, operation.TargetAccount); } #endregion } 

And the code:

 Operation[] operations = { transfer, withdrawal, proxy }; foreach (var operation in operations) { operation.Accept(visitor); } 

I know well that this is not an ideal solution. You still have to change the interface and visitors when adding new types. But you check compilation time and never miss a thing. One thing that would be very difficult to achieve with this method is to get pushed subtypes, but I'm not sure if this is a valid scenario. You will also have to modify this template to suit your specific scenario needs, but I will leave it to you.

+20
source share

Disabling lazy loading will return the actual instance instead of the NHibernate proxy.

eg..

mapping.Not.LazyLoad ();

or

 <class name="OrderLine" table="OrderLine" lazy="false" > 
+3
source share

Since the proxy is derived from an entity class, you can simply check the entity.GetType () object. BaseType to get a specific type.

-2
source share

All Articles