Question

There are multiple ways to work in the SP object model. The starting point for instantiating objects looks different when you look at different developers.

Many times I see people doing this:

SPList myList = SPContext.Current.Web.GetList("MyList");
SPWorkflowAssociation myWorkflowAssociation = SPContext.Current.Web....

so basically they are using multiple "SPContext.Current". I would assume this is not best practice and one should do multiple SPContext.Current within a using block?! Of course we should create our own object then, otherwise we would dispose the current web, so we should do this:

using (SPSite siteCollection = new SPSite(SPContext.Current.Web.Url))

instead of this:

using (SPWeb currentWeb = SPContext.Current.Web) //do not do this

Am I correct this far?

Furthermore I sometimes think that using SPContext.Current in general might be dangerous once you don't know your code will always be executing in the SharePoint context. I'm thinking of activating a feature via Powershell or running unit tests via little console applications (both no SPContext.Current!).

Would it then be "correct" to use the following?

using (SPWeb currentWeb = new SPWeb(<Url from some config file>)

Microsoft uses SPContext.Current here: Getting References to Sites, Web Applications, and Other Key Objects and also in their Disposing Objects article from the SP2010 Best Practices (very good read btw). But at the same time they use the aforementioned method of using a URL from somewhere to instantiate the objects instead of using SPContext.Current.


Would I be correct to assume the following rules:

  1. Instead of multiple instantiations of Sharepoint objects, make use of using blocks to properly dispose objects after usage
  2. Do not dispose objects you didn't create (i.e. SPContext.Current.Web)
  3. Use SPContext.Current when you know your code is running in SharePoint, when not sure use a static URL (or something else) to instantiate SP objects.
Était-ce utile?

La solution

Absolutely, if you have SPContext.Current, you don't need to dispose any objects inside. The point is, that SPContext.Current is binded to a SPRequest object, which represents current web page request. So basically, if you will dispose SPContext.Current.Web in your code, it will no longer be available for any other pending code.

But, if you will create your own object (for instance, based on SPContext.Current.Web.Url), another danger arises. For example, consider, that you have solutions from different vendors on your portal. Probably, some of them are using SPContext.Current objects. So if your custom solution runs first in the request execution sequence, and you're creating your own web, and then, say, you're updating Properties of this web, and then disposing it, SPContext.Current.Web properties are not refreshed and other solutions could rewrite them or read expired data.

Mere to illustrate this, here is some code:

// SPRequest and SPContext are created somewhere in depths of SharePoint

// .. your solution gained the control
using (SPSite site = new SPSite(SPContext.Current.Web.Url))
{
    using (SPWeb web = site.OpenWeb())
    {
        web.Properties["xx"] = "Hello";
        web.Properties.Update();
    }
}

// .. foreign solution gained the control
SPContext.Current.Web.Properties["yy"] = "Hello";
SPContext.Current.Web.Properties.Update();

The result: web.Properties["xx"] will not be saved.

So, in my opinion, you should always try to use SPContext.Current objects whenever it possible (or other context objects, like in event receivers, etc.) and should not ever dispose them, of course.

Drawing a line, I suppose the best behavour of code, which doesn't know where it will be executed (that is a common practice in different utility assemblies), is:

// url of the site must be passed as a parameter
bool contextEnabled = (SPContext.Current != null && SPContext.Current.Web.Url == url);

SPWeb web;
SPSite site;
if (contextEnabled)
{
    site = SPContext.Current.Site;
    web = SPContext.Current.Web;
}
else
{
    site = new Site(url);
    web = site.OpenWeb();
}

// .. do whatever you need here

if (!contextEnabled)
{
    web.Dispose();
    site.Dispose();
}

Your next step could be creating something like your own context object, making it disposable, and implementing your functionality there. So, you could end up with solution, similar to the following:

using (var context = new MySPContext("http://portal/"))
{
    context.Web.Properties[Constants.HelloWorldProperty] = "Hello world!";
    context.Web.Update();
}

We're using similar solution at work for common libraries, and so far, it satisfies all our needs.

Licencié sous: CC-BY-SA avec attribution
Non affilié à sharepoint.stackexchange
scroll top