Uploaded image for project: 'Spring.NET'
  1. Spring.NET
  2. SPRNET-735

Add AOP support for re-attaching/initializing nh-proxies to current session

    Details

    • Type: New Feature
    • Status: Open
    • Priority: Minor
    • Resolution: Unresolved
    • Affects Version/s: 1.1 RC1
    • Fix Version/s: 2.0
    • Component/s: Spring-NET-NH
    • Labels:
      None

      Description

      Below is some code contributed by Steinar Dragnes. It would be nice to e.g. have an attribute that may be applied to method return values and controls, how nh-proxies are treated:

      [ReturnsEntity(Depth=3)]
      Customer GetCustomer( Guid id)

      {.. }

      would ensure, that the returned customer object will be initialized (fully loaded) up to a nesting depth of 3.

      #region Internal helper class for LazyLoading.
      /// <summary>
      /// The LazyInitializer is responsible for initializing any given proxy. Needed resources
      /// are a valid session which is provided from SessionFactory and possibly the owning
      /// entity that holds the reference to the lazy proxy (for initializing collections).
      /// </summary>
      /// <author>Steinar Dragsnes (.NET)</author>
      internal class LazyInitializer
      {
      private static readonly ILog log = LogManager.GetLogger(typeof(LazyInitializer));

      /// <summary>
      /// Convenience method for loading proxies in entitys object graph. Providing fetch depth to speed up
      /// processing if only a shallow fetch is needed.
      /// </summary>
      /// <remarks>
      /// This is done by recursively looping through all properties in the object graph and
      /// examining if they are lazy loaded (represented by a proxy). It is at this moment unknown
      /// wether this approach is inefficient. This must be tested.
      /// </remarks>
      /// <param name="entity">
      /// This is done by recursively looping through all NHibernate mapped properties in the object graph and
      /// examining if they are lazy loaded (represented by a proxy). It is at this moment unknown
      /// wether this approach is inefficient. This must be tested.
      /// </param>
      /// <param name="maxFetchDepth">The search depth.</param>
      /// <param name="sessionFactory">The session factory used to extract the current session</param>
      /// <returns>A partly initialized entity, initialized to max fetch depth</returns>
      public static object InitializeEntity(object entity, int maxFetchDepth, ISessionFactory sessionFactory)

      { // Let's reduce the max-fetch depth to something tolerable... if (maxFetchDepth < 0 || maxFetchDepth > 20) maxFetchDepth = 20; // Okay, first we must identify all the proxies we want to initialize: ISession session = SessionFactoryUtils.GetSession(sessionFactory, false); ExtractNHMappedProperties(entity, 0, maxFetchDepth, false, session); return entity; }

      /// <summary>
      /// Convenience method for loading the complete object graph for an already initialized entity,
      /// where part's of the entity's object graph may be proxy instances.
      /// </summary>
      /// <remarks>
      /// This is done by recursively looping through all NHibernate mapped properties in the object graph and
      /// examining if they are lazy loaded (represented by a proxy). It is at this moment unknown
      /// wether this approach is inefficient. This must be tested.
      /// </remarks>
      /// <param name="entity">
      /// The entity to initialize. This must be an initialized entity object that holds lazy properties. From
      /// the LazyInitializer's scope, the entity is the top node in the object graph.
      /// </param>
      /// <param name="sessionFactory">The session factory used to extract the current session</param>
      /// <returns>The fully initialized entity</returns>
      public static object InitializeCompletely(object entity, ISessionFactory sessionFactory)

      { // Okay, first we must identify all the proxies we want to initialize: ISession session = SessionFactoryUtils.GetSession(sessionFactory, false); ExtractNHMappedProperties(entity, 0, 0, true, session); return entity; }

      /// <summary>
      /// Fully initialize the instance of Type type with primkey equal to id.
      /// </summary>
      /// <param name="type">The type to resolve and load</param>
      /// <param name="id">The primary key of the type to load</param>
      /// <param name="sessionFactory">The session factory used to extract the current session</param>
      /// <returns>The fully initialized entity</returns>
      public static object ImmediateLoad(Type type, int id, ISessionFactory sessionFactory)

      { object entity = SessionFactoryUtils.GetSession(sessionFactory, false).Load(type, id); return InitializeCompletely(entity, sessionFactory); }

      /// <summary>
      /// Search the object graph recursively for proxies, until a certain treshold has been reached.
      /// </summary>
      /// <param name="entity">The top node in the object graph where the search start.</param>
      /// <param name="depth">The current depth from the top node (which is depth 0)</param>
      /// <param name="maxDepth">The max search depth.</param>
      /// <param name="loadGraphCompletely">Bool flag indicating whether to ignore depth params</param>
      /// <param name="session">The current session to the db</param>
      private static void ExtractNHMappedProperties(object entity, int depth, int maxDepth, bool loadGraphCompletely, ISession session)
      {
      bool search;
      if (loadGraphCompletely) search = true;
      else search = (depth <= maxDepth);

      // Should we stay or should we go now?
      if (search)
      {
      // Check if the entity is a collection. If so, we must iterate the collection and
      // check the items in the collection. This will increase the depth level.
      Type[] interfaces = entity.GetType().GetInterfaces();
      foreach (Type iface in interfaces)
      {
      if (iface == typeof (ICollection))

      { ICollection collection = (ICollection) entity; foreach (object item in collection) ExtractNHMappedProperties(item, depth + 1, maxDepth, loadGraphCompletely, session); return; }

      }

      // If we get here, then we know that we are not working with a collection, and that the entity
      // holds properties we must search recursively. We are only interested in properties with NHAttributes.
      // Maybe there is a better way to specify this in the GetProperties call (so that we only get an array
      // of PropertyInfo's that have NH mappings).
      PropertyInfo[] props = entity.GetType().GetProperties();
      foreach (PropertyInfo prop in props)
      {
      if (prop.GetType().IsValueType) continue;
      if (prop.GetType().IsPrimitive) continue;

      BaseAttribute[] attrib = (BaseAttribute[]) prop.GetCustomAttributes(typeof (BaseAttribute), false);
      if (null != attrib && 0 < attrib.Length)
      {
      MethodInfo method = prop.GetGetMethod();
      if (null != method)
      {
      object proxy = method.Invoke(entity, new object[0]);
      if (!NHibernateUtil.IsInitialized(proxy))

      { Initialize(proxy, entity, session); }

      if (null != proxy) ExtractNHMappedProperties(proxy, depth + 1, maxDepth, loadGraphCompletely, session);
      }
      }
      }
      }
      }

      /// <summary>
      /// The core method delegating the hard lazy initialization work to the hibernate assemblies.
      /// </summary>
      /// <param name="proxy">The proxy to load</param>
      /// <param name="owner">The owning entity holding the reference</param>
      /// <param name="session">The current session to the db</param>
      private static void Initialize(object proxy, object owner, ISession session)
      {
      if (null != proxy)
      {
      Type[] interfaces = proxy.GetType().GetInterfaces();
      foreach (Type iface in interfaces)
      {
      if (iface == typeof(INHibernateProxy) || iface == typeof(IPersistentCollection))
      {
      if (!NHibernateUtil.IsInitialized(proxy))

      { if (iface == typeof(INHibernateProxy)) session.Lock(proxy, LockMode.None ); else session.Lock(owner, LockMode.None); NHibernateUtil.Initialize(proxy); }

      break;
      }
      }
      }
      }
      }
      #endregion

        Attachments

          Activity

            People

            • Assignee:
              mark.pollack Mark Pollack
              Reporter:
              oakinger Erich Eichinger
            • Votes:
              4 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

              • Created:
                Updated: