Tuesday, February 14, 2012

Working on locked site in site collection

Sometime we need to go through each site in site collection to update the properties bag, or whenever we need to loop on all site in site collection to do something, the code is simple like this:

foreach(SPSite site in webapp.Sites)
{
    //TODO: your code here
}

Everything works fine if Administrator did not lock any site in site collection. The administrator can go to Central Administration -> Application Management -> Site quote and lock, then perform the locking for one of site in site collection. Of course the code you won't work correctly and throw an exception:

Microsoft.SharePoint.SPException: Access to this Web site has been blocked.Please contact the administrator to resolve this problem. ---> System.Runtime.InteropServices.COMException (0x81020071): <nativehr>0x81020071</nativehr><nativestack></nativestack>Access to this Web site has been blocked.Please contact the administrator to resolve this problem.   at Microsoft.SharePoint.Library.SPRequestInternalClass.OpenWebInternal(String bstrUrl, Guid& pguidID, String& pbstrRequestAccessEmail, UInt32& pwebVersion, String& pbstrServerRelativeUrl, UInt32& pnLanguage, UInt32& pnLocale, String& pbstrDefaultTheme, String& pbstrDefaultThemeCSSUrl, String& 

To avoid the exception happens and the code will still continue if the site cannot access to, we should use the try/catch block to bypass the error:

foreach(SPSite site in webapp.Sites)
{
     try
     {
           //TODO: your code here
     }
     catch(Exception ex)
     {
         //Log the error here to know which site cannot be accessed
     }
     finally
     {
          //if you have an opening site to get the property from SPWeb, please close before dispose SPSite object
          site.Close();
          site.Dispose();
     }
}

The code above worked fine and everything seems to be right, but it's not the best practice here for a large site collection. Assume we need to loop for more than 1 thousand site in site collection, it's for sure that the code above is not good because everytime exception happens, the object exception must be initilized and go through final block to close and dispose object model. 

In this topic, I would like to check if the site cannot be accessed, we will bypass and continue on another site, the code will change:

foreach(SPSite site in webapp.Sites)
{

                FieldInfo fi = typeof(SPSite).GetField("bitField", BindingFlags.Instance | BindingFlags.NonPublic);
                if (fi == null)
                {
                    continue;
                }
                SPSite.BitField bitField = (SPSite.BitField)fi.GetValue(site);
                //check if locked site
                bool IsReadLock = (bitField & SPSite.BitField.readLock) > (SPSite.BitField)0u;
                bool IsWriteLock = (bitField & SPSite.BitField.writeLock) > (SPSite.BitField)0u;
                if (IsReadLock || IsWriteLock)
                {
                    continue;
                }

                //TODO: Your code here
}

Now, there is no try/catch block to receive the exception but there is no exception in this case, because we don't go and work on locked site.

Any another suggestion are welcome.

Hope this help.

No comments: