Friday, April 23, 2010

Populate lists the right way

Problem:
You need to save performance while bulding your objects, otherwise your code will experience major issues when more and more items add added to the list in the future.
The catch is not to instantiate through each object at each iteration.


Method 1 - really bad (SP.Items)

You iterate through each and every list item object in the list and you instantiate it every single time.

foreach (SPListItem lst in MyPictureList.Items)
{
    foreach (SPField field in MyPictureList.Fields)
    {
        writer.Write("Field: " + field.InternalName + " Field Type: " + field.Type.ToString() + " ");
        writer.Write("Content: " + lst[field.InternalName] + "");        
    }
    writer.Write("" + lst.Url+ "");
}

Method 2 - a bit bad (SPListCollection)

You instantiate SPListCollection, and then you iterate through the list using a for instead of a foreach.

SPListItemCollection splst = MyPictureList.Items;
SPFieldCollection spfields = MyPictureList.Fields;
for (int k = 0; k < splst.Count; k++)
{
    for (int j = 0; j < spfields.Count; j++)
    {
        writer.Write("Field: " + splst[k].Fields[j].InternalName + " Field Type: " + splst[k].Fields[j].Type.ToString() + " ");
        writer.Write("Content: " + splst[k][spfields[j].InternalName] + "");
    }
}

Method 3 - good (SPQuery)

You use SPQuery and make sure you use RowLimit.

SPQuery query = new SPQuery();
SPListItemCollection spListItems ; 
string lastItemIdOnPage = null; // Page position.
int itemCount = 2000
 
while (itemCount == 2000)
{
    // Include only the fields you will use.
    query.ViewFields = "";   
    query.RowLimit = 2000; // Only select the top 2000.
    // Include items in subfolder (if necessary).
    query.ViewAttributes = "Scope=\"Recursive\"";
    StringBuilder sb = new StringBuilder();
    // To make it order by ID and stop scanning the table, specify the OrderBy override attribute.
    sb.Append("");
    //.. Append more text as necessary ..
    query.Query = sb.ToString();
    // Get 2,000 more items.
 
    SPListItemCollectionPosition pos = new SPListItemCollectionPosition(lastItemIdOnPage);
    query.ListItemCollectionPosition = pos; //page info
    spListItems = spList.GetItems(query);
    lastItemIdOnPage = spListItems.ListItemCollectionPosition.PagingInfo;
    // Code to enumerate the spListItems.
    // If itemCount <2000 data-blogger-escaped-enumeration.="" data-blogger-escaped-finish="" data-blogger-escaped-span="" data-blogger-escaped-the="" data-blogger-escaped-we="">
    itemCount = spListItems.Count;
}

Or to get a single item, for which you don't know the ID but you do know the title:

private SPListItem GetItemByTitle(SPList list, string title)
{
   SPQuery query = new SPQuery();
   query.Query = "Title" + title + "";
   query.RowLimit = 1;
   query.ViewFields = "";
   return list.GetItems(query)[0];
}


Method 4 - even better (PortalSiteMapProvider)

You use PortalSiteMapProvider to get cached data.
However, if the data changes frequently, it will cause SharePoint to request an update, which may take longer than even SPQuery.

// Get the current SPWeb object. 
SPWeb curWeb = SPControl.GetContextWeb(HttpContext.Current); 

// Create the query. 
SPQuery curQry = new SPQuery(); 
curQry.Query = "
Hotel"; 

// Create an instance of PortalSiteMapProvider. 
PortalSiteMapProvider ps = PortalSiteMapProvider.WebSiteMapProvider; 
PortalWebSiteMapNode pNode = ps.FindSiteMapNode(curWeb.ServerRelativeUrl) as PortalWebSiteMapNode; 

// Retrieve the items. 

SiteMapNodeCollection pItems = ps.GetCachedListItemsByQuery(pNode, "myListName_NotID", curQry, curWeb); 

// Enumerate through all of the matches. 
foreach (PortalListItemSiteMapNode pItem in pItems)
{ 
   // Do something with each match.
}


Other Tips:

  • Use list.ItemCount instead of list.Items.Count
  • Use list.GetItemById instead of list.Items.GetItemById
  • Use SPWeb.Lists[GUID] instead of SPWeb.Lists[strDisplayName] (can get GUID from URL)
  • Do not enumerate entire SPList.Items collections or SPFolder.Files collections.
e.g.
SPList myList = web.Lists[web.GetList(web.ServerRelativeUrl + "/lists/MyList").ID];

Iterating properly
http://apmblog.compuware.com/2009/01/11/the-wrong-way-to-iterate-through-sharepoint-splist-items/

Coding best practices (MSDN)
http://msdn.microsoft.com/en-us/library/bb687949.aspx

Fastest way to get a specific item (by title)
http://sharepoint.stackexchange.com/questions/31599/fastest-way-to-retrieve-an-item-by-title

CAML Query Helper for SharePoint
http://spcamlqueryhelper.codeplex.com/

No comments:

Post a Comment