SPWeb object will be dispose everytime or not?
-
07-10-2020 - |
Frage
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
Lösung
Your code is wrong for two main reasons:
- You're returning an
SPWeb
object belonging to a closedSPSite
. That's not good because closing theSPSite
may actually close all childSPWeb
under the hood. Meaning the caller may be using a closedSPWeb
. - 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 differentSPWeb
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:
- The error you see can probably be explained by the
SPWeb
being closed implicitely by the parentSPSite
(itself closed explicitely in your property). What's the error message? - 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();
}
}
Andere Tipps
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.