How to use NHibernate Lazy Initializing Proxies with Web Services or WCF

Working with object trees is risky business when combined with remoting. Object trees often have very deep branches and circular references that will clog your pipes and overflow your stack when not handled properly.

NHibernate has addressed this same problem in the object-relational space with a very cool feature called lazy initialization, implemented using the Castle DynamicProxy library. By default, when an entity is loaded, its related entities are populated with proxies. The use of a proxy allows the entity to remain uninitialized until the moment one of its non-identifying properties is accessed, which in many cases is never – thus minimizing database queries. This strategy also eradicates the problem with loading an entire object tree from the database when only a single entity is needed.

Proxies become a problem when combined with Web services or WCF. Serialization of objects to XML requires knowledge of the data type to be serialized (accomplished with the KnownType attribute in WCF). The dynamically generated nHibernate proxies are not known to the serializer and thus a request to the service results in an exception such as System.Net.WebException: The underlying connection was closed: The connection was closed unexpectedly.

In order to make these entities safe for sending through the pipes, we must “unproxy” them and impose a limit to the object tree depth. The following utility class can be used for doing both by simply calling possiblyProxiedEntity.UnproxyObjectTree(mySessionFactory, maxDepth):

using System;
using NHibernate;
using NHibernate.Metadata;
using NHibernate.Proxy;
 
namespace Trentacular.Data.NHibernate.Util
{
    public static class NHibernateProxyUtils
    {
        /// <summary>
        /// Force initialization of a proxy or persistent collection.
        /// </summary>
        /// <param name="persistentObject">a persistable object, proxy, persistent collection or null</param>
        /// <exception cref="HibernateException">if we can't initialize the proxy at this time, eg. the Session was closed</exception>
        public static T Unproxy<T>(this T persistentObject)
        {
            var proxy = persistentObject as INHibernateProxy;
 
            if (proxy != null)
                return (T)proxy.HibernateLazyInitializer.GetImplementation();
 
            return persistentObject;
        }
 
        /// <summary>
        /// Gets the underlying class type of a persistent object that may be proxied
        /// </summary>
        public static Type GetUnproxiedType<T>(this T persistentObject)
        {
            var proxy = persistentObject as INHibernateProxy;
            if (proxy != null)
                return proxy.HibernateLazyInitializer.PersistentClass;
 
            return persistentObject.GetType();
        }
 
        /// <summary>
        /// Force initialzation of a possibly proxied object tree up to the maxDepth.
        /// Once the maxDepth is reached, entity properties will be replaced with
        /// placeholder objects having only the identifier property populated.
        /// </summary>
        public static T UnproxyObjectTree<T>(this T persistentObject, ISessionFactory sessionFactory, int maxDepth)
        {
            // Determine persistent type of the object
            var persistentType = persistentObject.GetUnproxiedType();
 
            var classMetadata = sessionFactory.GetClassMetadata(persistentType);
 
            // If we've already reached the max depth, we will return a placeholder object
            if (maxDepth < 0)
                return CreatePlaceholder(persistentObject, persistentType, classMetadata);
 
            // Now lets go ahead and make sure everything is unproxied
            var unproxiedObject = persistentObject.Unproxy();
 
            // Iterate through each property and unproxy entity types
            for (int i = 0; i < classMetadata.PropertyTypes.Length; i++)
            {
                var nhType = classMetadata.PropertyTypes[i];
                var propertyName = classMetadata.PropertyNames[i];
                var propertyInfo = persistentType.GetProperty(propertyName);
 
                // Unproxy of collections is not currently supported.  We set the collection property to null.
                if (nhType.IsCollectionType)
                {
                    propertyInfo.SetValue(unproxiedObject, null, null);
                    continue;
                }
 
                if (nhType.IsEntityType)
                {
                    var propertyValue = propertyInfo.GetValue(unproxiedObject, null);
 
                    if (propertyValue == null)
                        continue;
 
                    propertyInfo.SetValue(
                        unproxiedObject,
                        propertyValue.UnproxyObjectTree(sessionFactory, maxDepth - 1),
                        null
                        );
                }
            }
 
            return unproxiedObject;
        }
 
        /// <summary>
        /// Return an empty placeholder object with the Identifier set.  We can safely access the identifier
        /// property without the object being initialized.
        /// </summary>
        private static T CreatePlaceholder<T>(T persistentObject, Type persistentType, IClassMetadata classMetadata)
        {
            var placeholderObject = (T)Activator.CreateInstance(persistentType);
 
            if (classMetadata.HasIdentifierProperty)
            {
                var identifier = classMetadata.GetIdentifier(persistentObject, EntityMode.Poco);
                classMetadata.SetIdentifier(placeholderObject, identifier, EntityMode.Poco);
            }
 
            return placeholderObject;
        }
    }
}

There are several approaches on where and when this should take place depending on if you are using ASP.Net 2.0 Web services or WCF which I am not going to go into in this post. If you are working with WCF, Tim Scott has written about a good approach here.

Using jQuery with ASP.Net Demo Web site

I was recently asked to demonstrate how to use jQuery with ASP.Net, with a focus on AJAX.  So I spent a few hours and cooked up this demo web site.

The Web site demonstrates the following jQuery concepts:

  • Basic jQuery selectors and API usage
  • Wiring up javascript event handlers with jQuery
  • AJAX autocomplete textbox using the Autocomplete plugin
  • Maintaining AJAX navigation history and state using the History plugin
  • Loading dynamic html into a panel using the jQuery.load API function
  • Loading dynamic html into a modal dialog using the FaceBox plugin

The demo could be useful to both someone starting out with jQuery or an old-timer looking for some new AJAX approaches.  Let me know what you think.

Download

asp.net-jQuery-demo.zip
(Requires Visual Studio 2008 and the .Net Framework 3.5)

Preview

Trentacular is now on DreamHost

I have been hosting trentacular out of my house up until this past weekend.   While having complete control over the server was nice, I decided it was not worth having to worry about downtime, ISP reliability, etc.  So DreamHost won my business with their Independence Day special: a year of hosting (unlimited storage/bandwidth) and a domain renewal for just $9.24.   Yes, that is for the whole year!

If you experience any issues such as slowness or unavailablility, please let me know (which I realize might be difficult to do if the site is down ;) ).

SharePoint Site Settings Custom Actions Feature

I just released the third Feature that is now part of the Trentacular SharePoint 2007 Features CodePlex project: Site Settings Custom Actions

The Feature currently adds just a single custom action and page for exposing and managing Web (Site) properties with a similar UI as the previously released Farm Properties Editor (part of the Central Administration Extensions Feature).

Manage Farm Properties

The Web Properties Editor has proved convenient for externalizing site-specific configuration settings from which your custom features, whatever they may be, can read from. It also abstracts away the peculiarities of the SPWeb.Properties and SPWeb.AllProperties API which I wrote about in my last post – a must read if you are a SharePoint developer and haven’t already.

SharePoint: The Wicked SPWeb.Properties PropertyBag

*** This article is a must read for any developer reading or writing Web (Site) properties ***

If you haven’t already noticed, there are two different API properties on the SPWeb class – AllProperties (a Hashtable) and Properties (a PropertyBag). Apparently the AllProperties is meant to replace Properties, but Properties was left in place for backwards compatibility. Here’s where things get wicked …

The unconventional PropertyBag data type stores its keys in all lowercase, thus not supporting case-sensitive keys, while the conventional Hashtable does support case-sensitive keys. On top of that, while entries added to Properties get propagated to AllProperties with a lowercase key, entries added to AllProperties do not get propagated to Properties. So this what Microsoft gives us developer peons to work with. This is why consultants like myself stay employed.

If you are working with property entries that only your custom application will be reading and writing from, simply always use AllProperties and you will be good to go. However I often find myself having to work with properties that are read or written by SharePoint internals, and SharePoint itself is very inconsistent in its interaction with Web (Site) properties, probably due to its evolving nature being a rather old product as far as software is concerned.

So the best solution that I have come up with to date is to add your entries to both API properties. This ensures the entry will be present in both collections, and also ensures the key will have the correct case in AllProperties. I have found though in order to add to both, the order of the update API calls is important – you must first update the SPWeb object followed by updating the SPWeb.Properties PropertyBag. Performing the updates in the reverse order prevents an entry with a case-sensitive key from being added to AllProperties, and only the lowercase-keyed entry will be exposed in AllProperties. Now that you are probably thoroughly confused, here is the code:

// Add a property entry
web.Properties[key] = value;
web.AllProperties[key] = value;
web.Update();
web.Properties.Update();
 
// Remove a property entry
web.AllProperties.Remove(key);
web.Properties[key] = null;
web.Update();
web.Properties.Update();

Take a look, or straight up copy, my SharePoint utility class that abstracts away Web property interactions.

SharePoint Central Administration Extensions Feature

I just released the second feature that is now part of the Trentacular SharePoint 2007 Features CodePlex project: Central Administration Extensions

The solution currently deploys just a single custom action and page for managing farm properties. This has proved useful for us as a means to centralize configuration that is applicable across our entire SharePoint farm, such as custom application connection strings, log4net config file location, etc.

If you are a seasoned ASP.Net developer, you may also find the included Delegate Data Source useful. It essentially exposes the 4 main data source methods (select, insert, update, and delete) as events that can be handled directly in your code behind.

SharePoint: Differences Between Global and Web Application Targeted Solution Deployment

SharePoint solutions are either deployed globally or targeted to a particular web application, depending on whether the solution contains web application scoped Features. The deployment process behaves differently based on the type of deployment for the solution, and these differences have caused me quite a headache. Here are my findings that I think any SharePoint developer or administrator should be aware of when performing solution deployments.

Globally Deployed Solutions

When a solution is deployed globally, all SharePoint application pools, including Central Administration’s, are recycled automatically. This can be good and bad. This is good because any GAC installed DLL that has been upgraded needs to be reloaded. This can be bad though with regards to the availability of your entire SharePoint Farm.

In my particular case, I am working with a SharePoint administrator to deploy a globally deployed solution that is used by only a single web application. It is fine for this web application to be unavailable, as we have cleared it with our change control process and made announcements of the downtime, but we haven’t announced to users of the other SharePoint web applications in the Farm that their sites will be coming down along with it. So be forwarned.

Web Application Targeted Solutions

I have become fond of web application targeted solutions because they offer me a workaround to the above availability problem. I now even trick my solutions to be web application targeted that otherwise would be deployed globally by including a dummy web application scoped feature in them.

When a web application targeted solution is deployed or retracted, only the application pools of the targeted web applications are recycled.

Enterprise Farm Strategies

  • When upgrading solutions, the upgradesolution stsadm command only gets you so far. I’ve found it is best to fully retract and remove the old solution and then add and deploy the new solution in order to be sure your upgraded features actually take.
  • Avoid the -allcontenturls switch – When deploying and retracting a web application targeted solution, deploy or retract it only to those web applications that will use it … thus preventing unnecessary recycling of application pools. I was being lazy in my deployment script and instead of specifying the particular web application url, I used the -allcontenturls switch to retract my solution, hoping SharePoint would be smart enough to recycle only the application pools of the web applications the solution was actually retracted from. Bad assumption. They all get recycled.

A Big Gotcha that Got Me

If you are upgrading a web application targeted solution using the retract, remove, add, deploy strategy mentioned above and your web application scoped Features have associated Feature Receivers, then you MUST recycle the Central Administration application pool after removing the old solution and before adding the new solution. It is the Central Administration application pool that executes your web application scoped Feature Receivers, and if not recycled, it will continue to use the loaded cached old versions of assemblies updated by your solution.

SharePoint ASP.Net 3.5 Upgrade Feature

I’ve finally gotten around to starting a CodePlex project for publishing SharePoint features called Trentacular SharePoint 2007 Features. The first contribution to the project should reach a broad audience (within the SharePoint population that is) … an ASP.Net 3.5 Upgrade Feature.

The Feature is naturally scoped to a Web Application and adds ASP.Net 3.5 entries to the particular Web Application’s web.config file. It works for both WSS and MOSS and also supports clean deactivation by iterating through the SPWebConfigModification collection and removing modifications by owner instead of recreating the modifications. All modifications are done through a utility class called SPWebConfigModificationHelper which can be taken advantage of alone if you are writing your own modifications.

Before deciding to write this feature, I tried several others that have been published. Each had its own issues, from not supporting clean deactivation to simply just not adding the correct entries. The entries this feature writes were taken from the Visual Studio ASP.Net 3.5 Web.config template.

If you give this Feature a shot, please let me know how it goes.

Generic Feature Receiver for Features that Deploy WebParts

Greg Galipeau wrote a thorough post on cleaning up your Web Part Gallery that is the basis for this post. To summarize, Greg shares a Feature Receiver he uses as a generic Feature Receiver for all his WebPart Features that simply removes the WebPart having the same name as the Feature’s DisplayName when the Feature is deactivated.

There are a couple of issues with this though:

  1. It assumes that the WebPart has the same name as the Feature
  2. It limits the Feature to deploying just a single WebPart

An alternative approach would be to inspect the Feature’s element definitions and extract the exact WebPart names that were deployed, thus resolving the two issues noted above.

Below is the alternative Feature Receiver (it extends the BaseFeatureReceiver I mentioned in my previous post):

    public class WebPartFeatureReceiver : BaseFeatureReceiver<SPSite>
    {
        public override void FeatureDeactivating(SPSite site, SPFeatureReceiverProperties properties)
        {
            base.FeatureDeactivating(site, properties);
 
            var elements = properties.Definition.GetElementDefinitions(CultureInfo.CurrentCulture);
            var webparts = elements.Cast<SPElementDefinition>()
                .SelectMany(e => e.XmlDefinition.ChildNodes.Cast<XmlElement>()
                    .Where(n => n.Name.Equals("File"))
                    .Select(n => n.Attributes["Url"].Value)
                    )
                .ToList();
 
            var rootWeb = site.RootWeb;
            var wpGallery = rootWeb.Lists["Web Part Gallery"];
 
            var galleryItems = wpGallery.Items.Cast<SPListItem>()
                .Where(li => webparts.Contains(li.File.Name))
                .ToList();
 
            for (int i = galleryItems.Count - 1; i >= 0; i--)
            {
                var item = galleryItems[i];
                item.Delete();
            }
        }
    }

(SPWeb)properties.Feature.Parent no more … a handy Feature Receiver Base Class

If you are writing SharePoint feature receivers often, you will find there are several lines of code common to just about every feature receiver:

  1. A cast of properties.Feature.Parent to the appropriate scope (either SPWeb, SPSite, SPWebApplication, or SPFarm)
  2. Empty implementations of FeatureInstalled and FeatureUninstalling

Provided below is an abstract generic class that now serves as the base class for every feature receiver I write. It eliminates the redundant code by performing the cast for you based on the classes generic type parameter and also goes ahead and implements all the abstract methods, making it so that you to only need to override the actual methods you plan to handle. Your extending feature receiver now will look like the following:

    public class MyFeatureReceiver : BaseFeatureReceiver<SPWeb>
    {
        public override void FeatureDeactivating(SPWeb scope, SPFeatureReceiverProperties properties)
        {
            // Pointless, but what the heck
            base.FeatureDeactivating(scope, properties);
 
            // Now do something with your scope object, in this case an SPWeb
            ...
        }
    }

And now, the code for the BaseFeatureReceiver:

    /// <summary>
    /// Base class that makes the feature scope available as an argument to the
    /// FeatureActivated and FeatureDeactivating methods
    /// </summary>
    /// <typeparam name="T">
    /// T is the class type of the scope of the feature (SPFarm, SPWebApplication, SPSite, or SPWeb)
    /// </typeparam>
    public abstract class BaseFeatureReceiver<T> : SPFeatureReceiver
    {
        public sealed override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            FeatureActivated((T)properties.Feature.Parent, properties);
        }
 
        public virtual void FeatureActivated(T scope, SPFeatureReceiverProperties properties) { }
 
        public sealed override void FeatureDeactivating(SPFeatureReceiverProperties properties)
        {
            FeatureDeactivating((T)properties.Feature.Parent, properties);
        }
 
        public virtual void FeatureDeactivating(T scope, SPFeatureReceiverProperties properties) { }
 
        public override void FeatureInstalled(SPFeatureReceiverProperties properties) { }
        public override void FeatureUninstalling(SPFeatureReceiverProperties properties) { }
    }

Hope you find this helpful.