Monday, May 14, 2012

Best Practice fix issues from MSOCAF checking – Part 2


In the previous post, I introduced how to use MSOCAF to check the custom solution. There are a lot of issues after checked, and now I will focus on the issue that happens frequently. Read this article to improve your habits, change the mind and build a good product.
Case 1: use SPListItem.Update
Even though the Update() method is found in SPListItem and use to update any field in item. But it’s only available when we have separate updating on each item. Don’t use this method in loop statement, especially for large list. See the code
   1:  public void UpdateAll(SPListItemCollection items)
   2:  {
   3:       foreach (SPListItem itm in items)
   4:      {
   5:         itm["Count"] = 0;
   6:         itm.Update();
   7:       }
   8:  }
   9:  
It should be changed to use update command and execute by SPWeb object. Best practice code is as below:
   1:  public void UpdateAll(SPListItemCollection items)
   2:  {
   3:      StringBuilder builder = new StringBuilder();
   4:     
   5:      builder.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?><ows:Batch OnError=\"Return\">");
   6:   
   7:      foreach (SPListItem itm in items)
   8:      {
   9:   
  10:          string methodFormat = "<Method ID=\"{0}\">" +
  11:                                                "<SetList>{1}</SetList>" +
  12:                                                "<SetVar Name=\"Cmd\">Update</SetVar>" +
  13:                                                "<SetVar Name=\"ID\">{2}</SetVar>" +
  14:                                                "<SetVar Name=\"urn:schemas-microsoft-com:office:office#Count\">{3}</SetVar>" +
  15:                                                 "</Method>";
  16:   
  17:           builder.AppendFormat(methodFormat, itm["ID"], list.ID, itm["ID"], 0);
  18:      }
  19:   
  20:      builder.Append("</ows:Batch>");
  21:   
  22:      web.ProcessBatchData(builder.ToString());
  23}
  24:   
Because the Update() method is called in the loop, so that’s not best practice to improve performance. It is reported under custom rules of Microsoft Online when we run the MOSCAF checking. That is to be able to understand in the context of cloud environment. However, if there is a large list, please don’t add many thousands items into an update string, just less than 2000 that’s fine. To read more the performance when item is updated, please read here http://msdn.microsoft.com/en-us/library/cc404818(v=office.12).aspx
Case 2: SPListItemCollection\GetItemByID inside loop
See the code below:
   1:  SPList list = SPContext.Current.Web.Lists["Custom"];
   2:   
   3:  for (int i = 0; i < 10; i++)
   4:  {
   5:        SPListItem item = list.GetItemById(itm[i]);
   6:       //Doing something
   7}
   8:   
Almost developers think about the code above is very normal and nothing wrong. Of course that’s not, but it isn’t the best practice too. Because when we call list.GetItemById method that means the collection of SPListItem is retrieved and call again in the next index.
The code is changed:
   1:  SPList list = SPContext.Current.Web.Lists["Custom"];
   2:   
   3:  for (int i = 0; i < 10; i++)
   4:  {
   5:   
   6:       SPQuery query = new SPQuery();
   7:       query.RowLimit = 1;
   8:       query.Query = “<where><Eq><FieldRef Name=’ID’/><Value Type=’Number’>” + itm[i] + “</Value></Eq></Where>”;
   9:   
  10:        SPListItemCollection collection = list.GetItems(query);
  11:   
  12:        SPListItem item = collection[0];
  13:   
  14:        item["Count"] = 0;
  15:   
  16:        item.Update();
  17}
However, when we call the Update() method in loop statement we will get the error as case 1, we must combine case 1 and case 2 to fix together.
Case 3: Use SPList.Items
See the code:
   1:  public void DeleteSchemaItem(string url, string listName, int idItem)
   2:  {
   3:   
   4:       using (SPSite site = new SPSite(url))
   5:       {
   6:              site.CatchAccessDeniedException = false;
   7:   
   8:              using (SPWeb web = site.OpenWeb())
   9:              {
  10:                       SPList list = web.Lists[listName];
  11:   
  12:                       list.Items.DeleteItemById(idItem);
  13:              }
  14:        }
  15}
  16:   
The code above seem to work well for the functioning, however, it is only for very small list (a very little items in list). And with the large list, the code above is not good performance and need to be improved. Because when we call list.Items that means the collection of SPListItem is retrieved and after delete an item, this collection was re-modified.
The code is changed:
   1:  public void DeleteSchemaItem(string url, string listName, int idItem)
   2:  {
   3:     using (SPSite site = new SPSite(url))
   4:     {
   5:           site.CatchAccessDeniedException = false;
   6:           using (SPWeb web = site.OpenWeb())
   7:           {
   8:               SPList list = web.Lists[listName];
   9:               SPListItem item = list.GetItemById(idItem);
  10:               item.Delete();
  11:            }
  12:      }
  13}
  14:   
The code seems good with performance because it doesn’t reach to the very large collection at least.
Case 4: SPQuery object without RowLimit property
We often forget the most important property of SPQuery object, that is the RowLimit propery. If this property is not set, what is the default value? And as we know this value is accepted from range [1…2000]
It’s so excited when I work on this object, because most of us forgot the work as design of this object and thought to leave RowLimit property to wish getting all data when they match with the query CAML. And the code looks like:
   1:  SPQuery query = new SPQuery();
   2:   
   3:  query.Query = "<Where><Gt><FieldRef Name='ID'/><Value Type='Number'>1</Value></Gt></Where>";
   4:   
   5:  SPListItemCollection col = GetData(query, list);
The code will return all items in the list; however it will throw exception if the number of list item is greater than default value of Throttling in Central Administration configuration. The default value of Throttling is 5000 and this value is able to be overwritten by the code.
   1:  SPSecurity.RunWithElevatedPrivileges(delegate()
   2:  {
   3:       SPQuery query = new SPQuery();
   4:       query.Query = "<Where><Gt><FieldRef Name='ID'/><Value   Type='Number'>1</Value></Gt></Where>";
   5:       query.QueryThrottleMode = SPQueryThrottleOption.Override;
   6:       query.RowLimit = 5500;
   7:   
   8:       SPListItemCollection col = GetData(query, list);
   9});
  10:   
We must run the code under Full Control account, so the code is reverted to web application pool account to apply the overwrite throttling.
But with the MSOCAF, the RowLimit item is only valid in range [1...2000] unless you will get the error issue for performance. The important thing is the code above is a weakness security. For many purpose, we need to get all data with good performance and security but not violate the rule of MSOCAF.
The code is changed again:
   1:  SPQuery query = new SPQuery();
   2:   
   3:  query.RowLimit = 2000;
   4:  query.Query = "<Where><Gt><FieldRef Name='ID'/><Value Type='Number'>1</Value></Gt></Where>";
   5:   
   6:  do
   7:  {
   8:         SPListItemCollection col = GetData(query, list);
   9:         query.ListItemCollectionPosition = col.ListItemCollectionPosition;
  10}
  11:   
  12:  while (query.ListItemCollectionPosition != null);
  13:   
Case 5: Types that own disposable fields should be disposable
That means if we have implemented the class with some methods or properties return the object that implemented IDisposable interface, we also must inherit IDisposable and implement the code to dispose object or dispose object manually. In case, the class has shipped we can create a new method to manage the disposing object.
See the code
   1:  public class ItemDetail : ITemplate
   2:  {
   3:   
   4:       CheckBox chk;
   5:   
   6:       public void InstantiateIn(Control container)
   7:       {
   8:           chk = new CheckBox();
   9:           chk.ID = "chk";
  10:   
  11:           container.Controls.Add(chk);
  12:        }
  13}
  14:   
And create an object in CreateChildControl method of web part:
   1:  protected override void CreateChildControls()
   2:  {
   3:   
   4:      SPGridView grid = new SPGridView();
   5:      grid.AutoGenerateColumns = false;
   6:   
   7:      TemplateField tplFeild = new TemplateField();
   8:      tplFeild.HeaderTemplate = new ItemDetail();
   9:   
  10:      grid.Columns.Add(tplFeild);
  11}
  12:   
CheckBox is ASP.NET control and have implemented IDisposable interface, but we don’t care about disposing control when we add it into ASPX page because they are disposed when the page is disposed. And regarding code above, when the page is diposed there is only grid object is disposed and tplField.HeaderTemplate didn’t disposed, but chk object need to be disposed, so we must dispose manually.
See the code is changed:
   1:  public class ItemDetail : ITemplate, IDisposable
   2:  {
   3:   
   4:       CheckBox chk;
   5:   
   6:       private bool disposed = false;
   7:   
   8:       public void InstantiateIn(Control container)
   9:       {
  10:         chk = new CheckBox();
  11:         chk.ID = "chk";
  12:         container.Controls.Add(chk);
  13:        }
  14:   
  15:       public void Dispose()
  16:       {
  17:          Dispose(true);
  18:          GC.SuppressFinalize(this);
  19:        }
  20:   
  21:        protected virtual void Dispose(bool disposing)
  22:        {
  23:           if (!this.disposed)
  24:          {
  25:                if (disposing)
  26:                {
  27:                     if (chk != null) chk.Dispose();
  28:                 }
  29:               disposed = true;
  30:           }
  31:        }
  32:      
  33:        ~ItemDetail()
  34:        {
  35:            Dispose(false);
  36:         }
  37}
  38:   
We can use the keyword ‘using’ to dispose object automatically. There are also some objects do not need to dispose and we know that when method dispose is called, so it does nothing but we still use ‘using’ as a habit. Read more here http://weblogs.asp.net/pwilson/archive/2004/02/20/77435.aspx and http://msdn.microsoft.com/library/ms182172(VS.90).aspx
Case 6: Do not cast unnecessarily
There are many times we cast the variable to the same type and think about that as a safety. For example:
XmlElement tempElement;
   1:  foreach (XmlNode node in nodes)
   2:  {
   3:       string realDisplayName = ((XmlElement)node).Attributes["DisplayName"].InnerText;
   4}
   5:   
It is so clearly that variable named ‘node’ is an element of XML document, even though it is retrieved from XmlNode collection (XMLNodeList) but it’s still an element. Thus, the casting value of node variable to XmlElement is not necessary.
Another example in SharePoint:
   1:  SPField field = list.Fields.GetFieldByInternalName("OurColumnsName");
   2:   
   3:  if (field is SPFieldDateTime)
   4:  {
   5:       SPFieldDateTime fDateTime = field as SPFieldDateTime;
   6:       //DO SOMETHING
   7}
   8:   
The ‘is’ statement is casted object and the assignment object fDateTime with ‘as’ is duplicated casting. So we must change the code, please read this document to know how to change the code to match the rule of MSOCAF. Here is the link: http://msdn.microsoft.com/library/ms182271(VS.90).aspx

Best Practice fix issues from MSOCAF checking – Part 1

Best Practice fix issues from MSOCAF checking – Part 1

Microsoft SharePoint Online Code Analysis Framework (MSOCAF) is a tool published by Microsoft to check the code with the rules which are pre-defined online. This tool is not a desktop application, and because it is using an extensible framework, so every time the program runs, it updates the rules from SharePoint online engineering team. There are a lot of areas like memory management, security vulnerabilities, exception handling…that MSOCAF focuses on. MSOCAF is including FxCop, CAT.NET and SPDisposeCheck checking.

Use this tool to test deployment, roll back deployment and submit your code to Microsoft to prepare to push your product on cloud.

To download MSOCAF tool, click this link: https://caf.sharepoint.microsoftonline.com/Default.aspx

Installation


After downloaded, click on setup.exe

clip_image001

After installed, there is an icon on desktop. Click on icon to start running the tool. For the very first time, it will update the rules from SharePoint Online engineering team, so it takes a little long time. Be patient! But for the next time, its speed is faster and if there is no internet connection, all the rules are loaded from cache. We also could not customize, change or remove any rules from MSOCAF.

First screen of MSOCAF look like this:

clip_image003

Before analyze the code of your product, prepare the folders structure and copy all related files.


  • Create root folder, this is a top most folder. You can create it anywhere in your local drive.
  • Create a child folder named “Release documents” and copy all files *.doc (*.docx), *.xml for application note or guidance to use your product.
  • Create a child folder named “Solutions artifacts” and copy all files *.dll, *.exe, *.pdb, *.stp, *.wsp, *.xml of your product. Note: all of referent dlls or files should be copied to here unless it will stop analyzing.
  • Create a child folder named “Installation scripts” including almost files to install your product. It also include PowerShell scripting, xslt or sql script.
  • Create a child folder named “Test documents” and copy all test case documents or any files related to product.


The root folder and Solutions artifacts folder are required existing, other folders does not need to create. If you don’t have any files about test case, release documents or installation scripts, don’t worry about that, just copy dll and exe to Solutions artifacts folder and leave other folders to be empty. For the next time, to check another project, let delete all files in Solutions artifacts and copy dll’s another project then paste into this folder.

Analyze the code


Click on Analyze button, the structure folder guideline window shows and click next (if you are acquainted with usage this tool, stick the check box “Skip this step in the future”) and the rules review window shows (you are also doing stick the check box “Skip this step in the future) then click next. The next screen, select a Root folder that you’ve created before. Finally, click on analyze to start checking.

After checking finished, there is a folder named “Caf reports” is created and there are 3 report files inside.
Note: for the next time checking, MSOCAF will notify a warning about the folder “Caf reports” is existed, that means you had run checking before and if you would like to load previous report or run new analysis. Depend on your purpose to decide what you need to do.

clip_image004

If you click on Run New Analysis, you will get the next window warning/error about the structure of folders. Because you have copied incorrect files and pasted into the folder. For example, “Release documents” is only hosted by *.doc, *.docx or *.xml but there is another file type. So, MSOCAF shows error folder structure. However, if there is not important for your purpose checking, you could answer ‘Yes’ to continue and bypass any error about folder structure.

clip_image005

We recommend that you should correct any error before start analyzing the code.

Now, take a rest and drink a cup of coffee to wait until the MSOCAF finish checking. The result look like:

clip_image007

To view the issue easily, use filter dropdown control to select exactly assembly to view. You also scope down the issue to view by using test cases dropdown to select the rule. Failed issue only contains error issues, so please focus on error as a high priority and warning as low priority.

I love this tool very much because it not only shows the error and bad code in our code but also shows how to fix the issue and explain why it is recognized as an error.

clip_image008

Just click a link attached to know how the best practice code is and follow the instruction to fix your issues. However, some easy issues just have the very short explanation and no link is attached.

Wednesday, April 11, 2012

SharePoint 2013 recommendations

SharePoint 2013 recommendations

SharePoint 2010 had been improved a lot of things that SharePoint 2007 could not be done as well. However, the collaboration for now is not an important thing that needs to be cared as the top of feature. But the social, sharing and publishing are the most important. Almost people transfer from other system to SharePoint step by step and there are so many reasons to convert to SharePoint.

Until now, SharePoint 2010 still had exposed the social weakness and that is the main reason to leave SharePoint and continue using very old system, especially is open source system.

I hope that SharePoint 2013 (Office 15) will improve the following things:


  • Improve the social for discussion board plus. Take it easy to post a comment, reply or any response on Discussion list.
  • Add more event receiver for SharePoint Object Model, especially when I restore all items from recyle bin. In recently, no event receiver catch the handling for that, it's really difficult to check, or do something when they are restored.
  • Still hard to handle the large list and forest sites in site collection. The performance still is so bad or even not responded. 

Saturday, March 10, 2012

The new things in SharePoint 15 Object Model - Part 2

In the previous post, I introduced some new methods in come class’ in SharePoint 15 Object Model. In this post, I would like to show more new methods that help you prepare to upgrade your product to new baseline and improve your performance.

1. SPFieldLookup: As you known, the schema of this column contains information the original list and column that this column is looking to. If you need to get the original list and column, you must read and analyst data from schema. In new object model, you just get from method GetJsonClientFormFieldSchema() method and parse it to Json object. It’s easily to get an object and improve your code.

2. Microsoft SharePoint Foundation 15 has exposed the new class named “SPFileRightsManagementSettings” for getting information about the download file. This class defined 5 properties and without any method, I don’t know why Microsoft did not introduce any method for this class.


  • AllowPrint: this property indicates whether the file is able to print or not. 
  • AllowScript: allow the viewer can be run the script while reviewing the document. 
  • AllowWritecopy: allow the viewer can write down a copy. 
  • DocumentAccessExpireDay: get the day which document is available to download. 
  • GroupName: show the group which has permission can be view this document. 


There is only 1 noticed for any object of this class is not guaranteed about thread safe, that means this object must be disposed and released the memory after used.

Because companies often have restrictions that require their files to be stored in nonencrypted formats, SharePoint Foundation does not store files in encrypted, rights-managed file formats. However, SharePoint Foundation calls an IRM protector to convert the stored file to an encrypted format each time a user downloads the file. Similarly, when a user uploads a rights-managed copy of a file, SharePoint Foundation calls the appropriate IRM protector to convert that copy to a nonencrypted format before it is stored. As a result, you do not need to create custom solutions to enable searching or archiving of document libraries where IRM is enabled. Storing the files in nonencrypted format ensures that the current Search indexing service is able to crawl content stored on the servers. Search results are already scoped to user permissions, so the user never sees search results that include content to which they do not have some level of access.”

In SharePoint Foundation, IRM is enabled for any files in document libraries and Site Collection Admin can choose the method to protect the downloadable file. Thus, IRM must be enabled at the document libraries level. For more information read more here

3. SPSolutionExporter: used to export a web site as a template and export a workflow as a workflow template. This class expose new method called “ExportFolderToList” to export a specific folder as an object to the gallery.

public static string ExportFolderToList( SPFolder folder, string solutionFileName, string title, string description, SPList solutionList ) 

For being time, Microsoft does not show how big the folder can be exported. The folder size exports as a solution file template must be less than the maximum size allowed unless it throws exception exceeds the maximum allowed.

Thursday, March 8, 2012

The new things in SharePoint 15 Object Model - Part 1

From the technical preview document, SharePoint 2015 has been updated a lot of methods that help developers improving the code.

1. The first thing is from SPUtility, this class gives a supplementary the method GetLayoutsFolder to specify the layout folder.

SPUtility.GetLayoutsFolder(SPWeb);
SPUtility.GetLayoutsFolder(SPSite);

If site.CompatibilityLevel returns the value greater than or equal 15, the method GetLayoutsFolder() returns "_layouts/site.CompatibilityLevel", otherwise returns "_layouts".

In aditional, there are 3 new propertie:

ContextCompatibilityLevel: Gets the compatibility level of the context site ContextLayoutsFolder: Gets the versioned layouts folder for the context site CurrentThemeFolderName:  Gets the name of the current theme folder

2.  ASP.NET has been supported Control.Page to regist the script on client by ClientScript. Almost developers often use this object to regist, but with SharePoint 2015 object model we can use the new class to replace Control.Page class.

SPPageContentManager also has fully methods to perform like Control.Page such as IsClientScriptBlockRegistered, IsStartupScriptRegistered, RegisterClientScriptBlock, RegisterHiddenField, RegisterScriptFile, RegisterStartupScript

3. SharePoint 2015 also expose new properties of SPWorkflowEventProperties
  •  AssociationId: Gets the identifier of the workflow SPWorkflowAssociation. 
  • AssociationName: Gets the name of the workflow SPWorkflowAssociation.
  • CorrelationId: Gets the correlation identifier for the workflow, if supplied.
  • Created: Gets the DateTime value for when the workflow was created.
  • ItemGuid: Gets the identifier of the workflow ParentItem.
  • ItemId: Gets the identifier of the workflow ParentItem.
  • ListId: Gets the identifier of the workflow ParentList.
  • Modified: Gets the last DateTime the workflow was modified
  • StatusText: Gets the string that represents the status of the workflow.
  • StatusValue: Gets the integer that represents the workflow status.
  • TemplateId: Gets the identifier of the workflow association base template.
This helps us improve performance a lot because these properties can be access directly when workflow is running. Of course, defends on the purpose of your project but these properties reduce the time to build a workflow a lot. That's good upgrading.

Tuesday, March 6, 2012

Could not create site or list by reserved name?


As you know, Windows reserved some words as a device configuration such as CON (CON known as console), PRN (PRN known as printer device)…Thus, we cannot create a folder or file name which the name were registered. I don’t think this issue affected to SharePoint because before SharePoint save anything, it always validates the input data to be sure all texts are correct.

When I show my issue to my colleague, they were for sure that I didn’t configure SharePoint farm correctly or my SharePoint server had have a big problem. I didn’t have any comment or idea for this issue, but I was ensured that there was a problem with SharePoint.

Let’s try with these steps:
  • ·        Create a site or a list with named “CON”, “COM1” or any name which are reserved by windows.
  • ·        No validation and continue saving, but after finish, the page redirected look like:



In general, SharePoint will show the message after validated data input, but in this case, SharePoint didn’t. Anyone knows is it a work as design or SharePoint issue?

Tuesday, February 28, 2012

How to detect SharePoint platform is MOSS or WSS?

SharePoint platform wasn't show special thing to determine MOSS or WSS is running currently. But we can check the feature in feature collection to detect the default feature for MOSS. When we install MOSS, there are 2 features as default in MOSS flatform:OssNaviation and Publishing. Just check these feature, the code snippet look like:



 private bool CheckMossOrWss()
        {
            bool isMOSS = false;

            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                SPFeatureDefinitionCollection features = SPContext.Current.Site.WebApplication.Farm.FeatureDefinitions;
                SPFeatureDefinition docSetfea = features.FirstOrDefault(f => (f.DisplayName == "DocSet" || f.DisplayName == "Publishing"));
                 
                isMOSS = (docSetfea != null);
           
            });
            return isMOSS;
        }


Hope this help.

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.