Sunday, December 21, 2008

Best Practices disposing Sharepoint objects - Reference Guide

Frequently developers use the Sharepoint object model either to read or write data into Share point database via Lists/Libraries. Here comes the need to dispose Sharepoint objects, even though they are created as managed objects, these objects use unmanaged code and memory to perform a majority of their work.

Because the smaller managed part of the object does not put memory pressure on the garbage collector, the garbage collector does not release the object from memory in a timely manner. The object's use of a large amount of unmanaged memory can cause some of the unusual behaviors described as follows:

  • Frequent recycles of the Microsoft Windows SharePoint Services application pool, especially during peak usage
  • Application crashes that appear as heap corruption in the debugger
  • High memory use for Microsoft Internet Information Services (IIS) worker processes
  • Poor system and application performance
Applications that work with IDisposable objects in MOSS must dispose of the objects when the applications finish using them. You should not rely on the garbage collector to release them from memory automatically.

Unusual behaviors mentioned above occur when Sharepoint objects are not disposed off properly or explicitly by the devlopers. Here is a reference guide on Sharepoint Coding best practices with relevant scenarios and examples of each type:

Three techniques to ensure that your objects get disposed properly:

  • Dispose method
  • using clause
  • try, catch, and finally blocks
Whenever you wish to free memory resources, call Dispose method, instead of the Close method , internally Dispose method calls Close method itself, the reason being that standard .NET Framework process calls the Dispose method to free memory resources associated with object.

Thankfully Microsoft has recently launched Sharepoint Dispose Checker utility. It will ensure that you have properly disposed objects so that you aren't consuming more memory. All developers should run this utility after they use Sharepoint object model. Click here to download SPDispose Utility.

NOTE : This post is applicable for both SharePoint 2010 and SharePoint 2007

Best Practice #1: Use the Using Clause for all Sharepoint objects that implement the IDisposable interface
C# Coding snippet

String str;

using(SPSite oSPsite = new SPSite("http://server"))
{
using(SPWeb oSPWeb = oSPSite.OpenWeb())
{
str = oSPWeb.Title;
str = oSPWeb.Url;
}
}

Best Practice #2: Use the try/catch/finally blocks. When you use try blocks, it is important to add a finally block to ensure that all objects are disposed off properly:

C# Coding snippet

String str;
SPSite oSPSite = null;
SPWeb oSPWeb = null;

try
{
oSPSite = new SPSite("http://server");
oSPWeb = oSPSite.OpenWeb(..);

str = oSPWeb.Title;
}
catch(Exception e)
{
}
finally
{
if (oSPWeb != null)
oSPWeb.Dispose();

if (oSPSite != null)
oSPSite.Dispose();
}

Best Practice #3: Using Response.Redirect in the try block will never execute the finally block, so it is important, to dispose off all objects before the redirection occurs:

C# Coding snippet
String str;
SPSite oSPSite = null;
SPWeb oSPWeb = null;
bool bDoRedirection = true;

try
{
oSPSite = new SPSite("http://server");
oSPWeb = oSPSite.OpenWeb(..);

str = oSPWeb.Title;
if(bDoRedirection)
{
if (oSPWeb != null)
oSPWeb.Dispose();

if (oSPSite != null)
oSPSite.Dispose();

Response.Redirect("newpage.aspx");
}
}
catch(Exception e)
{
}
finally
{
if (oSPWeb != null)
oSPWeb.Dispose();

if (oSPSite != null)
oSPSite.Dispose();
}

Best Practice #4: Whenever you create an object with a new operation, the creating application must dispose it off

C# Coding snippet

SPSite oSPSite = new SPSite("http://server");

... additional processing on SPSite ...

oSPSite.Dispose();

alternatively, this is a better approach

C# Coding snippet

using(SPSite oSPSite = new SPSite("http://server"))
{
... additional processing on SPSite ...
}

Best Practice #5: For Site.OpenWeb method, you need to dispose it off explicitly.

C# Coding snippet

String str;
SPSite oSPSite = new SPSite("http://server");
SPWeb oSPWeb = oSPSite.OpenWeb();

str = oSPWeb.Title;
str = oSPWeb.Url;

... additional processing on SPWeb ...

oSPWeb.Dispose();
oSPSite.Dispose();

Alternatively you may use the Using clause too for better readibility and automatic disposition of objects:

C# Coding snippet

String str;
using(SPSite oSPSite = new SPSite("http://server"))
{
using(SPWeb oSPWeb = oSPSite.OpenWeb())
{
str = oSPWeb.Title;
str = oSPWeb.Url;

... additional processing on SPWeb ...
}
}

Best Practice #6: An exception to the rule is that One should not explicitly dispose SPSite.RootWeb, as it is automatically disposed off.

Similary one should not explicity dispose SPContext.Current.Site and SPContext.Current.Web as they are handles automatically by Sharepoint and .NET framework.

C# Bad Coding Practice Snippet:

void SPContextBADPractice()
{
SPSite siteCollection = SPContext.Current.Site;
siteCollection.Dispose(); // DO NOT DO THIS
SPWeb web = SPContext.Current.Web;
web.Dispose(); // DO NOT DO THIS
}


C# Good Coding Practice Snippet:

void SPContextBestPractice()
{
SPSite siteCollection = SPContext.Current.Site;
SPWeb web = SPContext.Current.Web;
// Do NOT call Dispose()
}
Best Practice #7: SPControl.GetContextSite(Context) and GetContextWeb(Context) return SPSite and SPWeb respectively, they do not need an explicit call to Dispose(), they will be disposed automatically

C# Bad Coding Practice Snippet:

void SPControlBADPractice()
{
SPSite siteCollection = SPControl.GetContextSite(Context);
siteCollection.Dispose();   // DO NOT DO THIS
SPWeb web = SPControl.GetContextWeb(Context);
web.Dispose();  // DO NOT DO THIS
}

C# Good Coding Practice Snippet:

void SPControlBestPractice()
{
SPSite siteCollection = SPControl.GetContextSite(Context);
SPWeb web = SPControl.GetContextWeb(Context);
// Do NOT call Dispose()
}

Best Practice #8: SPWeb.ParentWeb  returns SPWeb, and needs to be disposed off explicitly using either Dispose() or the using clause

C# Bad Coding Practice Snippet:
void ParentWebLeak()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb outerWeb = siteCollection.OpenWeb())
{
SPWeb parentWeb = outerWeb.ParentWeb; // Internal reference to SPWeb parentWeb
string sTitle = parentWeb.Title;
string sUrl = parentWeb.Url;
// SPWeb object parentWeb leaked
} // SPWeb object outerWeb.Dispose() automatically called
}  // SPSite object siteCollection.Dispose() automatically called 
}

C# Good Coding Practice Snippet:
void ParentWebBestPractice()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb outerWeb = siteCollection.OpenWeb())
{
using (SPWeb parentWeb = outerWeb.ParentWeb) // Internal reference to SPWeb parentWeb
{
string sTitle = parentWeb.Title;
string sUrl = parentWeb.Url;
} // SPWeb object parentWeb.Dispose() automatically called
} // SPWeb object outerWeb.Dispose() automatically called
}  // SPSite object siteCollection.Dispose() automatically called 
}

Best Practice #9: SPWeb.Webs returns an SPWeb object, practically used in drilling down sub-sites within a Site

C# Bad Coding Practice Snippet:
void WebsLeak()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb outerWeb = siteCollection.OpenWeb())
{
foreach (SPWeb innerWeb in outerWeb.Webs)
{
// SPWeb innerWeb leak
}
} // SPWeb object outerWeb.Dispose() automatically called
}  // SPSite object siteCollection.Dispose() automatically called 
}


C# Good Coding Practice Snippet:
void WebsBestPractice()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb outerWeb = siteCollection.OpenWeb())
{
foreach (SPWeb innerWeb in outerWeb.Webs)
{
innerWeb.Dispose();
}
} // SPWeb object outerWeb.Dispose() automatically called
}  // SPSite object siteCollection.Dispose() automatically called 
}

Best Practice #10: AllWebs[] Indexer returns SPWeb
object that needs to be disposed to avoid aggregation of memory which
can lead to memory pressure when running on a site collection with
large number of sub sites. Practically used in Enumerating or iterating all webs in a site collection.

C# Bad Coding Practice Snippet:

void AllWebsForEachLeakBestPractices()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb outerWeb = siteCollection.OpenWeb())
{
foreach (SPWeb innerWeb in siteCollection.AllWebs)
{
 // Explicit Dispose must be called to avoid aggregation of memory
}
} // SPWeb object outerWeb.Dispose() automatically called
}  // SPSite object siteCollection.Dispose() automatically called 
}


C# Good Coding Practice Snippet:

void AllWebsForEachLeakBestPractices()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb outerWeb = siteCollection.OpenWeb())
{
foreach (SPWeb innerWeb in siteCollection.AllWebs)
{
innerWeb.Dispose();   // Explicit Dispose must be called to avoid aggregation of memory
}
} // SPWeb object outerWeb.Dispose() automatically called
}  // SPSite object siteCollection.Dispose() automatically called 
}

C# Bad Coding Practice Snippet:

void AllWebsIndexerLeak()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
SPWeb web = siteCollection.AllWebs[0];
// SPWeb web leaked
}  // SPSite object siteCollection.Dispose() automatically called 
}

C# Good Coding Practice Snippet:

void AllWebsIndexerBestPractice()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb web = siteCollection.AllWebs[0])
{
} // SPWeb object web.Dispose() automatically called
}  // SPSite object siteCollection.Dispose() automatically called 
}

Best Practice #11: AllWebs.Add() returns a instance of SPWeb object which needs to be disposed.

C# Bad Coding Practice Snippet:
void AllWebsAddLeak()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
SPWeb web = siteCollection.AllWebs.Add("site-relative URL");
// SPWeb web Leaked
}  // SPSite object siteCollection.Dispose() automatically called 
}

C# Good Coding Practice Snippet:
void AllWebsAddBestPractice()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb web = siteCollection.AllWebs.Add("site-relative URL"))
{
} // SPWeb object web.Dispose() automatically called
}  // SPSite object siteCollection.Dispose() automatically called 
}

Best Practice #12: OpenWeb() returns a SPWeb object which needs to be disposed.

C# Bad Coding Practice Snippet:
void OpenWebLeak()
{
using (SPWeb web = new SPSite(SPContext.Current.Web.Url).OpenWeb())
{
// SPSite leaked !
} // SPWeb object web.Dispose() automatically called
}

C# Good Coding Practice Snippet:
void OpenWebBestPractice()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
using (SPWeb web = siteCollection.OpenWeb())
{
} // SPWeb object web.Dispose() automatically called
}  // SPSite object siteCollection.Dispose() automatically called
}

Best Practice #13:new SPSite() - Instantiating SPSite objects with the new operator needs to be disposed.

C# Bad Coding Practice Snippet:
void CreatingSPSiteLeak()
{
SPSite siteCollection = new SPSite("http://moss");
// siteCollection leaked
}

C# Good Coding Practice Snippet:
void CreatingSPSiteExplicitDisposeBestPractice()
{
SPSite siteCollection = new SPSite("http://moss");
siteCollection.Dispose();
}

void CreatingSPSiteWithAutomaticDisposeBestPractice()
{
using (SPSite siteCollection = new SPSite("http://moss"))
{
} // SPSite object siteCollection.Dispose() automatically called
}

Do Not Dispose Guidance for SharePoint 2010 & 2007:(Updated on 23/2/2011):
Do not dispose the following listed objects explicitly:

•SPContext.Current.Site

•SPContext.Current.Web

•SPContext.Site

•SPContext.Web

•SPControl.GetContextWeb(..)

•SPControl.GetContextSite(..)

•SPFeatureReceiverProperties.Feature.Parent

•SPItemEventProperties.ListItem.Web

•SPList.BreakRoleInheritance()

◦Do not call list.ParentWeb.Dispose()

•SPListEventProperties.Web

•SPListEventProperties.List.Web

•SPSite.RootWeb

◦Problems may occur when SPContext.Web has equality to the SPContext.Web.. make sure you dispose of SPSite and it will cleanup sub webs automatically

•SPSite.LockIssue

•SPSite.Owner

•SPSite.SecondaryContact

•SPWeb.ParentWeb

•SPWebEventProperties.Web

Changes:

•Microsoft.SharePoint.WebControls.SiteAdminsitrationSelector.CurrentItem

◦When used with WSS 3.0 you must call Dispose(), with SharePoint Foundation 2010 you don’t.

•Event Receivers and properties.OpenWeb()

◦WSS 3.0: When you call properties.OpenWeb() the returned SPWeb will need to call Dispose()

◦SharePoint Foundation 2010: Use the newly introduced SPItemEventProperties.Web property instead of SPItemEventProperties.OpenWeb() for better performance and to avoid the need to call Dispose().


Sources: Compiled from MSDN and Roger Lamb's blog site If you find the Coding practices helpfull, please drop in your valuable comments...

Sunday, December 14, 2008

SQL Exception while applying WSS Service Pack 1

Its been a long busy month, actually i am working on our company's Intranet portal, its an end-to-end implementation, so we started right from scratch and iteration 1 is about to complete and we are nearing the virgin roll-out.

I was actually applying the WSS Service Pack 1 on our Test environment and it bombed the site, luckily i had taken the full back-up of my site collection with Content database using stsadm, (click here for more information how to take back-ups and restore the entire site collection using STSADM utility)

Immediately as i was applying the WSS 3.0 SP 1 update, the configuration wizard threw this nasty error :
An error has occurred while establishing a connection to the server. When connecting to SQL Server 2005, this failure may be caused by the fact that under the default settings SQL Server does not allow remote connections. (provider: Named Pipes Provider, error: 40 - Could not open a connection to SQL Server)

I tried out the following tricks (Source: MSDN)

Enable remote connections for SQL Server 2005 Express or SQL Server 2005 Developer Edition

  1. Click Start, point to Programs, point to Microsoft SQL Server 2005, point to Configuration Tools, and then click SQL Server Surface Area Configuration.
  2. On the SQL Server 2005 Surface Area Configuration page, click Surface Area Configuration for Services and Connections.
  3. On the Surface Area Configuration for Services and Connections page, expand Database Engine, click Remote Connections, click Local and remote connections, click the appropriate protocol to enable for your environment, and then click Apply.

    Note Click OK when you receive the following message:
    Changes to Connection Settings will not take effect until you restart the Database Engine service.
  4. On the Surface Area Configuration for Services and Connections page, expand Database Engine, click Service, click Stop, wait until the MSSQLSERVER service stops, and then click Start to restart the MSSQLSERVER service.

Enable the SQL Server Browser service

If you are running SQL Server 2005 by using an instance name and you are not using a specific TCP/IP port number in your connection string, you must enable the SQL Server Browser service to allow for remote connections. For example, SQL Server 2005 Express is installed with a default instance name of Computer Name\SQLEXPRESS. You are only required to enable the SQL Server Browser service one time, regardless of how many instances of SQL Server 2005 you are running. To enable the SQL Server Browser service, follow these steps.

Important These steps may increase your security risk. These steps may also make your computer or your network more vulnerable to attack by malicious users or by malicious software such as viruses. We recommend the process that this article describes to enable programs to operate as they are designed to, or to implement specific program capabilities. Before you make these changes, we recommend that you evaluate the risks that are associated with implementing this process in your particular environment. If you choose to implement this process, take any appropriate additional steps to help protect your system. We recommend that you use this process only if you really require this process.

  1. Click Start, point to Programs, point to Microsoft SQL Server 2005, point to Configuration Tools, and then click SQL Server Surface Area Configuration.
  2. On the SQL Server 2005 Surface Area Configuration page, click Surface Area Configuration for Services and Connections.
  3. On the Surface Area Configuration for Services and Connections page, click SQL Server Browser, click Automatic for Startup type, and then click Apply.

    Note When you click the Automatic option, the SQL Server Browser service starts automatically every time that you start Microsoft Windows.
  4. Click Start, and then click OK.
Note When you run the SQL Server Browser service on a computer, the computer displays the instance names and the connection information for each instance of SQL Server that is running on the computer. This risk can be reduced by not enabling the SQL Server Browser service and by connecting to the instance of SQL Server directly through an assigned TCP port. Connecting directly to an instance of SQL Server through a TCP port is beyond the scope of this article. For more information about the SQL Server Browser server and connecting to an instance of SQL Server, see the following topics in SQL Server Books Online:
  • SQL Server Browser Service
  • Connecting to the SQL Server Database Engine
  • Client Network Configuration

Create exceptions in Windows Firewall

These steps apply to the version of Windows Firewall that is included in Windows XP Service Pack 2 (SP2) and in Windows Server 2003. If you are using a different firewall system, see your firewall documentation for more information.

If you are running a firewall on the computer that is running SQL Server 2005, external connections to SQL Server 2005 will be blocked unless SQL Server 2005 and the SQL Server Browser service can communicate through the firewall. You must create an exception for each instance of SQL Server 2005 that you want to accept remote connections and an exception for the SQL Server Browser service.

SQL Server 2005 uses an instance ID as part of the path when you install its program files. To create an exception for each instance of SQL Server, you must identify the correct instance ID. To obtain an instance ID, follow these steps:

  1. Click Start, point to Programs, point to Microsoft SQL Server 2005, point to Configuration Tools, and then click SQL Server Configuration Manager.
  2. In SQL Server Configuration Manager, click the SQL Server Browser service in the right pane, right-click the instance name in the main window, and then click Properties.
  3. On the SQL Server Browser Properties page, click the Advanced tab, locate the instance ID in the property list, and then click OK.
To open Windows Firewall, click Start, click Run, type firewall.cpl, and then click OK.

Create an exception for SQL Server 2005 in Windows Firewall

To create an exception for SQL Server 2005 in Windows Firewall, follow these steps:
  1. In Windows Firewall, click the Exceptions tab, and then click Add Program.
  2. In the Add a Program window, click Browse.
  3. Click the C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Binn\sqlservr.exe executable program, click Open, and then click OK.

    Note The path may be different depending on where SQL Server 2005 is installed. MSSQL.1 is a placeholder for the instance ID that you obtained in step 3 of the previous procedure.
  4. Repeat steps 1 through 3 for each instance of SQL Server 2005 that needs an exception.

Create an exception for the SQL Server Browser service in Windows Firewall

To create an exception for the SQL Server Browser service in Windows Firewall, follow these steps:
  1. In Windows Firewall, click the Exceptions tab, and then click Add Program.
  2. In the Add a Program window, click Browse.
  3. Click the C:\Program Files\Microsoft SQL Server\90\Shared\sqlbrowser.exe executable program, click Open, and then click OK.

    Note The path may be different depending on where SQL Server 2005 is installed See the actual implementation of the same by Pinal Dave at: http://blog.sqlauthority.com/2008/08/09/sql-server-fix-error-1326-cannot-connect-to-database-server-error-40-could-not-open-a-connection-to-sql-server/
I verified all of the above, but all in vain..
.
I checked the Event Viewer and it told me something else in addition to above,
The reason I get the errors is that the content databases are not at the same version as the application files causing an inability for SharePoint to read the database. Fortunately, you can solve this by running a nice little stsadm command:
so here is what i did,
went to the Command prompt mode, typed the path of the12 Hive, i.e : cd C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN), then gave the following command,

stsadm.exe -o upgrade -forceupgrade -inplace


After Operation successfull completed message, Do an iisreset

Everything started worked beautifully like before. It was another gruilling day of the already hectic month, i immediately made up my mind on writing this article, so as to save my readers from hours of googling and needless stress.

In case, your issue is not resolved, try this: http://support.microsoft.com/kb/934838/en-us