I was refactoring some SharePoint code today when I ran into a situation that I think illustrates the challenges with developing in SharePoint. Since object creation and destruction are some of the more expensive operations in SharePoint, I was looking through the code for places where SPSite and SPWeb objects were being unnecessarily created and destroyed. There were a lot of places in the code that followed this typical pattern:
using (SPSite site = new SPSite("http://rootsite"))
using (SPWeb web = site.OpenWeb())
//Do something ....
Now this isn't bad. The site object and the web object are properly wrapped in a using statement so these objects will be properly disposed after we're done using them just like the best practices tells us to do. But in a lot of the places where this type of code was written, it was unnecessary to create brand new site and web objects. We could have just as easily used the SPContext.Current.Site and the SPContext.Current.Web objects. So that's what I started to do. Any code where SPSite and SPWeb objects were being created and destroyed, I refactored to use SPContext.Current.Site and SPContext.Current.Web if it was possible to do so. Again, following best practices, I removed the using statements and made sure not to explicitly call Dispose() on these objects since SharePoint will take care of disposing these objects, unlike the SPSite and SPWeb objects created explicitly.
Every once and a while, I would rebuild, redeploy, and retest the code to make sure nothing was broken. Everything seemed to be going swimmingly. However, after refactoring a particular web part, I started to get the dreaded "Trying to use an SPWeb object that has been closed or disposed ..." error. I checked and re-checked the code. I didn't explicitly call Dispose() on any object in the web part, let alone any SPWeb or SPSite objects. I didn't have any SPWeb or SPSite objects wrapped in a using statement either. So what gave? What was breaking? The code didn't spit out this error before so why was I getting this error now after the refactoring?
I started to go over the places where I changed the code when this particular snippet in the code caught my eye:
using (FullTextSqlQuery sqlQuery = new FullTextSqlQuery(SPContext.Current.Site))
//Do something ...
So using Reflector, I disassembled the FullTextSqlQuery class to see what the heck was going on. What I found was that when you construct a new FullTextSqlQuery object (and other objects that derive from the Query object), the SPSite object passed into the constructor is saved to an instance variable (m_site). And when you dispose of your FullTextSqlQuery object instance, it's Dispose() method calls m_site.Dispose(). Since I passed in the SPContext.Current.Site object, when some other code in the request pipeline needed to use this Site object, an error occurred because it had already been disposed.
So the lesson is if you're using the classes derived from the Query class (from either the Microsoft.SharePoint.Search.Query or the Microsoft.Office.Server.Search.Query namespaces) and you use the constructor that accepts an SPSite object, then be careful not to pass an SPSite instance that is needed after the Query object is destroyed. That means never pass it the SPContext.Current.Site object nor any SPSite object you construct yourself that you'll need after the Query object's lifetime.
Now back to my original point: This is simply another example of what can make SharePoint development difficult. When using the SharePoint object model, it is often necessary to know the implementation of the SharePoint object you're trying to use otherwise you may not know how to handle the object properly (as in the case of disposing SPWeb and SPSite objects) or may not know of the side effects introduced by the object. In this case, I had to crack open the code for the FullTextSqlQuery class in order for me to see what side effects occurred when using this class. And this is not the first time I've had to do that (I'll post about another case in a future blog).
I think that SharePoint is a fantastic product and definitely a viable development platform. In my opinion, the benefits and features of SharePoint far outweigh the challenges the product can sometimes present (from a development perspective). But API/Framework developers should strive to avoid forcing their consumers to have to know the inner workings of their framework in order to use it correctly and in this regard, I think there is still some room for improvement in SharePoint.