-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 13 posts ] 
Author Message
 Post subject: Raising LazyInitializationException
PostPosted: Sat Jul 07, 2007 10:35 pm 
Beginner
Beginner

Joined: Tue Jan 02, 2007 5:53 pm
Posts: 42
Location: Bergen, Norway
Hi!

I'm currently writing an ExceptionManager that will through AOP intervene and try to catch a set of predefined exceptions, among these are the LazyInitializationException. While debugging this exception I've been trying to find out what kind of information the exception provides. For instance, I would like to see the object containing the lazy property that holds the proxy. Is it possible and would it be convenient to change the LazyInitializationException to hold such information?

It would be really great for contexts where you do not have the object instance accessible. In a regular try-catch you would know which object that contains the uninitialized property, but using AOP such information is not available. If the exception itself could provide this information then solving this centrally once for all would be really simple.

I might be mistaken here, maybe you already wrap this object into the LazyInitializationException, but so far I haven't seen any traces of it while debugging. If I'm wrong, then I would be very thankful if someone explained to me how this object could be extracted from the exception object being thrown.

Thanks,
Steinar.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jul 08, 2007 2:28 am 
Contributor
Contributor

Joined: Wed May 11, 2005 4:59 pm
Posts: 1766
Location: Prague, Czech Republic
No, LIE doesn't contain the object that caused it. I'm not sure even what you could use it for. I suppose we could put the class name and the id into the exception, for debugging purposes. We can't put the referencing entity there because we don't know it.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jul 08, 2007 5:12 am 
Beginner
Beginner

Joined: Tue Jan 02, 2007 5:53 pm
Posts: 42
Location: Bergen, Norway
Okay, I just took a look at the source, at the LazyInitializer class and I think I understand what you mean. Still, having the type, id and MethodInfo would help very much. In a normal try-catch I can do this:
Code:
// Testcase playing with LIE
[Test]
public void TestLoadingUser()
{
    IUser user = dao.LoadUserByUserName("MrMySQL");
    ICollection<IRole> roles = user.Roles;

    try
    {
        Assert.Greater(0, roles.Count);
    } catch(LazyInitializationException e)
    {            
        aNewSession.Lock(user, LockMode.Read);
        Assert.AreEqual(0, roles.Count);
    }
}


Here I know that I must re-attach the user with the session. If the LIE instance knew the user instance (or at least type and id) when being raised then it could be extracted as an advice would typically contain:

Code:
public object Invoke(IMethodInvocation invocation)
{
    ExceptionHandlerAttribute policy = null;
    ...
    ...
    ...

    try
    {
        object returnValue = invocation.Proceed();
        return returnValue;
    }
    catch(Exception e)
    {            
        return ExceptionManager.HandleException(e, policy, invocation);
    }
}


In the handle exception method you would have enough information to call the service layer as you normally would and initialize the lazy property. Being able to extract type and id (and maybe MethodInfo as well) from LIE would also make it very easy to resolve lazy load exceptions.

This approach works nicely for other exception types at the moment, and can be compared with the Exception Handling Application Block described in Microsoft Best Practices.

I hope I made more sense in this post.

_________________
Cheers,
Steinar.


Last edited by steinard on Mon Jul 09, 2007 2:58 am, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Sun Jul 08, 2007 6:34 am 
Contributor
Contributor

Joined: Wed May 11, 2005 4:59 pm
Posts: 1766
Location: Prague, Czech Republic
No, you certainly can't do that with NHibernate. You can't use the session after an exception occurs.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jul 08, 2007 6:37 am 
Contributor
Contributor

Joined: Wed May 11, 2005 4:59 pm
Posts: 1766
Location: Prague, Czech Republic
You should treat LazyInitializationException as a programming error - much like NullReferenceException. Would you try to recover from a NullReferenceException in this way?


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jul 08, 2007 6:49 am 
Beginner
Beginner

Joined: Tue Jan 02, 2007 5:53 pm
Posts: 42
Location: Bergen, Norway
I do not want to use that session, I will need to create a new session and since I'm using OSIV I'll automatically get a new session anyway when the instruction pointer enters the service layer.

Currently I'm doing all this manually, having try-catches several places in the presentation model (at the client side) as I cannot for all use-cases know all the data I need to load in one fetch. Wrapping this into AOP should be very doable, all I need is some additional info about what to load if an exception is thrown.

_________________
Cheers,
Steinar.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jul 08, 2007 7:03 am 
Beginner
Beginner

Joined: Tue Jan 02, 2007 5:53 pm
Posts: 42
Location: Bergen, Norway
Quote:
You should treat LazyInitializationException as a programming error - much like NullReferenceException. Would you try to recover from a NullReferenceException in this way?


Normally I would not do that, but sure, it can be done. If you get a nullpointer and you have enough information to do something else in the catch to recover, and you actually do that in your catch, then you could put this into an advice so that you do not have to decorate your code with try-catches in many places, especially if this is something that happens several places and have the same solution.

Simple example to illustrate this:
Code:
[ExceptionHandler(ExpectedExceptionType = typeof(NullReferenceException), ExceptionHandler = typeof(DefaultCarProvider))]
public bool DriveCar()
{
   return car.DriveCar(influence);
}


You could just as well have written this:
Code:
public bool DriveCar()
{
   try
   {
      return car.DriveCar(influence);
   } catch(NullReferenceException e)
   {
      car = DefaultCarProvider.ProvideDefaultCar();
      return car.DriveCar(influence);
   }
}


Sure you might say that you should not be able to drive the car if the car really is null (because you do not have a car)! Even though this is a bad example, then I've seen many places that nulls are recovered with default values.

Anyway, I really disagree with you when you say that LIE should be treated like a NullReferenceException. The reason we are getting this exception in the first place is that we've chosen, maybe due to performance, to avoid preloading that specific property (described by .hbm.xml mappings). Now, for those 10% situations in a use-case where we really need this property, we should be able to get the data (on some kind of postload) to complete the use-case. I would not treat LIE as a NullReferenceException because you do not know if it it really is a null value for the property or just a value that you chose to postpone the loading of.

_________________
Cheers,
Steinar.


Last edited by steinard on Sun Jul 08, 2007 12:45 pm, edited 4 times in total.

Top
 Profile  
 
 Post subject:
PostPosted: Sun Jul 08, 2007 7:34 am 
Beginner
Beginner

Joined: Tue Jan 02, 2007 5:53 pm
Posts: 42
Location: Bergen, Norway
Basically, I want to use AOP to solve this problem described in the NHibernate reference documentation.

Quote:
You may also attach a previously loaded object to a new ISession with Update() or Lock() before accessing unitialized collections (or other proxies). NHibernate can not do this automatically, as it would introduce ad hoc transaction semantics!


I'm doing this anyway with a lot more lines of code then what I can get away with using AOP. Just a question, since I'm getting curious here, how would you solve the problem of getting a lazy property? Would you not use lazy-loading at all? Would you try to determine the needed object graph to be fetched for every use-case?

_________________
Cheers,
Steinar.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jul 08, 2007 2:59 pm 
Beginner
Beginner

Joined: Tue Jan 02, 2007 5:53 pm
Posts: 42
Location: Bergen, Norway
Hi!

Quote:
No, LIE doesn't contain the object that caused it. I'm not sure even what you could use it for. I suppose we could put the class name and the id into the exception, for debugging purposes. We can't put the referencing entity there because we don't know it.


The abstract base class AbstractPersistentCollection that are one of two classes in the NHibernate assembly that throws LIE actually holds the referencing entity instance, and a small modification on how LIE is instantiated made it possible to get more context information into the exception.

Code:
      /// <summary>
      /// Initialize the collection, if possible, wrapping any exceptions
      /// in a runtime exception
      /// </summary>
      /// <param name="writing">currently obsolete</param>
      /// <exception cref="LazyInitializationException">if we cannot initialize</exception>
      protected void Initialize(bool writing)
      {
         if (!initialized)
         {
            if (initializing) throw new LazyInitializationException(owner, "cannot access loading collection", null);
            if (IsConnectedToSession)
            {
               if (session.IsConnected)
               {
                  try
                  {
                     session.InitializeCollection(this, writing);
                  }
                  catch (Exception e)
                  {
                     log.Error("Failed to lazily initialize a collection", e);
                     throw new LazyInitializationException(owner, "Failed to lazily initialize a collection", e);
                  }
               }
               else
               {
                  throw new LazyInitializationException(owner, "Failed to lazily initialize a collection - session is disconnected", null);
               }
            }
            else
            {
               throw new LazyInitializationException(owner, "Failed to lazily initialize a collection - no session", null);
            }
         }
      }



The other class throwing LIE is the LazyInitializer. This class has information about the referencing entity's type and the object id:
Code:
      /// <summary>
      /// Perform an ImmediateLoad of the actual object for the Proxy.
      /// </summary>
      /// <exception cref="HibernateException">
      /// Thrown when the Proxy has no Session or the Session is closed or disconnected.
      /// </exception>
      public void Initialize()
      {
         if (!initialized)
         {
            if (_session == null)
            {
               throw new LazyInitializationException(_persistentClass, _id, "Could not initialize proxy - no Session.");
            }
            else if (!_session.IsOpen)
            {
               throw new LazyInitializationException(_persistentClass, _id, "Could not initialize proxy - the owning Session was closed.");
            }
            else if (!_session.IsConnected)
            {
               throw new LazyInitializationException(_persistentClass, _id, "Could not initialize proxy - the owning Session is disconnected.");
            }
            else
            {
               _target = _session.ImmediateLoad(_persistentClass, _id);
               CheckTargetState();
               initialized = true;
            }
         }
         else
         {
            CheckTargetState();
         }
      }



So all the information is really there and I would like to understand why this information should not be provided when throwing LIE. With more context information, I can now make lazy loading automatic when accessing a lazy property without a session, with owning session closed etc. In my modified version of NHibernate I've got this information available, and can handle the exception through reflection and AOP instead of the tedious try-catch statements that get scattered around in the presentation model.

_________________
Cheers,
Steinar.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 10, 2007 7:26 pm 
Beginner
Beginner

Joined: Tue Jan 02, 2007 5:53 pm
Posts: 42
Location: Bergen, Norway
Anyone else who has any views on this topic? It should be quite important as I find numerous posts on people asking about the LazyIntitializationException, both here and in other forums...

_________________
Cheers,
Steinar.


Top
 Profile  
 
 Post subject:
PostPosted: Sat Jul 14, 2007 3:50 am 
Expert
Expert

Joined: Tue Aug 23, 2005 5:52 am
Posts: 335
Steinard,

Have you created a JIRA issue about this? It doesn't look like something that is going to break code going forward and provides useful additional information.

Symon.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Jul 15, 2007 6:25 pm 
Beginner
Beginner

Joined: Tue Jan 02, 2007 5:53 pm
Posts: 42
Location: Bergen, Norway
Hi!

I've just created an issue for this case. Please vote for it here: http://jira.nhibernate.org/browse/NH-1069

_________________
Cheers,
Steinar.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 17, 2007 9:21 am 
Newbie

Joined: Tue Jul 17, 2007 9:11 am
Posts: 5
sergey wrote:
You should treat LazyInitializationException as a programming error - much like NullReferenceException. Would you try to recover from a NullReferenceException in this way?


Hi

I may just precise the purpose : we need to "transparantly" reattach a dettached object to a new session, for beeing able to "transparantly" lazy-load some datas (ie a collection). So, when we detect a LIE, we catch it, reattach the object, and load the missing datas. But for beeing able to do this, we need some more information into the LIE.

Maybe it's clearer this way.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 13 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.