Question

I have following code in my web part. I want to know whether this property _web will create object each time whenever I use it. If yes then will it be disposed every time.

private SPWeb _web
{
    get
    {
        using (SPSite site = new SPSite(<SiteURL>))
        {
            return site.OpenWeb();
        }
    }
}

Will appreciate immediate response.

Update:
@Evariste: We are using this code in SharePoint 2013 application page deployed as site collection feature.

Getting below stack trace of error in ULS.

Unexpected 00000000-0000-0000-0000-000000000000 Stack trace: at Microsoft.SharePoint.SPListCollection.GetListByName(String strListName, Boolean bThrowException) at AgreementApprovalRequest.Layouts.AgreementApprovalRequest.CreateNewAggApprRequest.GetDetailFromUI() at AgreementApprovalRequest.Layouts.AgreementApprovalRequest.CreateNewAggApprRequest.saveFormDetails() at AgreementApprovalRequest.Layouts.AgreementApprovalRequest.CreateNewAggApprRequest.BtnSubmit_Click(Object sender, EventArgs e) at System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument) at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
at System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
at System.Web.UI.Page.ProcessRequest() at System.Web.UI.Page.ProcessRequest(HttpContext context) at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) at System.Web.HttpApplication.PipelineStepManager.ResumeSteps(Exception error) at System.Web.HttpApplication.BeginProcessRequestNotification(HttpContext context, AsyncCallback cb) at System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context) at System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags) at System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags) at System.Web.Hosting.UnsafeIISMethods.MgdIndicateCompletion(IntPtr pHandler, RequestNotificationStatus& notificationStatus) at System.Web.Hosting.UnsafeIISMethods.MgdIndicateCompletion(IntPtr pHandler, RequestNotificationStatus& notificationStatus) at System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags) at System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags) 78635e9d-13dc-e0ea-fac5-34124891f447

Code:
Getting Error in two different places. Shown below

SPList docLibrary = _web.Lists[ListName.LegalAgreementTemplate.ToString()];  
using (SPSite site = new SPSite(_web.Url))

Regards,
Nafe

Was it helpful?

Solution

Your code is wrong for two main reasons:

  1. You're returning an SPWeb object belonging to a closed SPSite. That's not good because closing the SPSite may actually close all child SPWeb under the hood. Meaning the caller may be using a closed SPWeb.
  2. Having a property like that lets callers assume they can call it as often as they want, e.g. like this: if(_web....){_web...} witch is not a good practice here since _web has actually a huge cost. And it' always returning a different SPWeb object, so for instance such code _web.Title = "New Title"; _web.Update() won't have any effect.

If you tell us in which context your code is used (Web context like in a Web part, SharePoint exe context like for a timer job, or unknown like a workflow) we can try to find a better design. But something like the following should always do the trick:

class SPWrapper : IDisposable
{
   string m_siteUrl;
   public SPWrapper(string siteUrl)
   {
      m_siteUrl = siteUrl;
   }

   SPSite m_site = null;
   SPSite Site
   {
      get
      {
         if(m_site == null)
            m_site = new SPSite(m_siteUrl);
         return m_site;
      }
   }

   SPWeb m_web = null;
   SPWeb Web
   {
      get
      {
         if(m_web == null)
            m_web = Site.OpenWeb();
         return m_web;
      }
   }

   public void Dispose()
   {
      if(m_web != null)
         m_web.Dispose();
      if(m_site != null)
         m_site.Dispose();
   }
}

It's up to the caller to actually call .Dispose on SPWrapper when it's done with all operations on Web and Site.
Another version would be to use a singleton on SPWrapper but that's only if you're not in a Web context (in Web context don't cache/store_in_static variables any SP object).
In a Web context, you could use another trick, like associating one SPWrapper to the page, and disposing it from the UnLoad (or OnError) event of the page.

Update
OK, after reviewing additional info you provided:

  1. The error you see can probably be explained by the SPWeb being closed implicitely by the parent SPSite (itself closed explicitely in your property). What's the error message?
  2. As you're in an application page (_layouts, i.e. Web context), her's a piece of code more suited for your needs (implementing the idea of opening the Web once, and closing it at unload time). However, please, one question: why don't you use the contextual SPWeb (SPContext.Current.Web) here?

The code for an application page. The idea with it is to make you layout pages inherit from MyLayoutsPageBase instead of LayoutsPageBase:

public class MyLayoutsPageBase : LayoutsPageBase
{
   protected virtual string UrlOfSiteToOpen
   {
      get
      {
         // TODO
         return <site_url>; 
      }
   }

   SPSite m_site = null;
   protected SPSite Site
   {
      get
      {
         if(m_site == null)
            m_site = new SPSite(UrlOfSiteToOpen);
         return m_site;
      }
   }

   SPWeb m_web = null;
   protected SPWeb Web
   {
      get
      {
         if(m_web == null)
            m_web = Site.OpenWeb();
         return m_web;
      }
   }

   protected override void OnError(EventArgs e)
   {
      base.OnError(e);
      CleanUpSPObjects();
   }

   protected override void OnUnload(EventArgs e)
   {
      base.OnUnload(e);
      CleanUpSPObjects();
   }

   private void CleanUpSPObjects() // We close any SPSite/SPWeb we open explicitely
   {
       if(m_web != null)
         m_web.Dispose();
      if(m_site != null)
         m_site.Dispose();
   }
}

OTHER TIPS

No. The instance of your Web you create with .OpenWeb() will not be disposed, only your site will be disposed. You need to call .Dispose() on your web when you don't need it anyomore or put using around it.

Licensed under: CC-BY-SA with attribution
Not affiliated with sharepoint.stackexchange
scroll top