Friday, November 28, 2008

MOSS Barcode Image Timing Out in Large Document Libraries

I'm working with a few large libraries in our SharePoint farm, one of which is well over 200,000 documents and climbing.  In these libraries, we have the barcode policy turned on, however on the DispForm.aspx page for viewing a document's properties, the barcode image never loads.  Eventually after about 5 minutes, it will timeout and show a broken link like this.

brokenbarcode

In the web front end event log, this error is recorded:

0x0234  Windows SharePoint Services  Database  8sli  Monitorable    
List item query elapsed time: 371854 milliseconds, Additional data 
(if available): Query HRESULT: 80131530 List internal name, flags, 
and URL: {GUID}, flags=0x0000000004c01088, 
URL=https://[hostname]/_layouts/barcodeimagefromitem.aspx?
doc=[DocGuid]&list=[ListGuid]

After troubleshooting, searching the web, and dealing with Microsoft support all to no gain, it was decided to break a best practice and overwrite one of Microsoft's layout pages, namely the troublesome barcodeimagefromitem.aspx. To this I want to give many props to my colleague, Jamie Burkholder, who did much of the work on this fix. This is the solution we arrived at.

If you view the source for this layout page, it's not much to look at.  It primarily links to a code behind page, that we don't have the code for, so the only option is to rename the page to barcodeimagefromitem.old.aspx so we can create our own page.

Here is the code we used to replace the original.

<%@ Page language="c#" %>

<%@ Assembly Name="Microsoft.Office.Policy, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"%>

<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<%@ Import Namespace="System.Diagnostics" %>
<%@ Import Namespace="System.Drawing" %>
<%@ Import Namespace="System.Drawing.Imaging" %>
<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Import Namespace="Microsoft.Office.RecordsManagement" %>
<%@ Import Namespace="Microsoft.Office.RecordsManagement.PolicyFeatures" %>


<script runat="server">
     
        
        #region "Event Handlers"
     
        //----------------------------------------------
        //  Event Handlers
        //----------------------------------------------
     
        void Page_Load(object sender, System.EventArgs e)
       {
           string listId = Request["List"].ToString();         // collect the list guid from URL
           string docId = Request["Doc"].ToString();                // collect the doc guid from URL
           string barCodeValue = "";
    
           Guid uidListId = new Guid(listId);
           SPList targetList = null;
           SPListItem item;
           System.Drawing.Image    barCodeImage = null;
    
           
           // get list from the guid
           targetList = SPContext.Current.Web.Lists[uidListId];
                   
           // Lookup the document
           if (targetList != null)
           {
               item = targetList.GetItemByUniqueId(new Guid(docId)); 
               
               if (item != null)
               {
                   barCodeValue = Barcode.GetBarcodeValue(item);
                   barCodeImage = Barcode.GetBarcodeImage(item);  
               }
    
               Response.ContentType = "image/jpeg";
               barCodeImage.Save(Response.OutputStream, ImageFormat.Jpeg);
           }
       }    
       
       #endregion
       
</script> <html xmlns="http://www.w3.org/1999/xhtml"> <head id="Head1" runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> </form> </body> </html>

While developing this code, we were able to recreate the same timeout issue as the Microsoft version.  Here is what happened.  Originally, when we were collecting the SPListItem from the SharePoint API we used this line:

item = targetList.Items[new Guid(docId)];        /* Never use this!!!!! */

This works fine until you start putting thousands of documents in the same library.  When this happens, this line will timeout and you will never get your list item.  However, Microsoft knew what they were doing and they gave us an alternative to this that performs like a champ even when there are hundreds of thousands of list items.  Here is the example:

item = targetList.GetItemByUniqueId(new Guid(docId));

This is a solid work around until Microsoft patches the bug, however remember when modifying these layout pages that Microsoft will likely come along and overwrite your work with the next patch.

Best Regards,

-Mark Gabriel

1 comment:

Anonymous said...

This Jamie guys sounds amazing!!!

Wendy