XSL performance issue when using custom Itemstyle Xsl with ContentQueryWebPart.

In one of my support cases customer was using a modified "ItemStyle.xsl" sheet to display the first 1000 characters of a SharePoint Blog posting using the content query webpart. This method works during small blog postings, however when the customer posts large blog posts (tested with 8000 lines) it eats the system memory until the Application Pool crashes.

Just in case you are new to customizing CQWP following posts are great places to start:

https://msdn.microsoft.com/en-us/library/bb850574%28office.12%29.aspx

https://msdn.microsoft.com/en-us/library/aa981241.aspx

https://www.heathersolomon.com/blog/articles/customitemstyle.aspx

· The ULS logs exhibit these entries when the issue occurs :

Potentially excessive number of SPRequest objects (11) currently unreleased on thread 1

......Microsoft.SharePoint.Publishing.WebControls.ContentByQueryWebPart.getWeb(String webUrl) at Microsoft.SharePoint.Publishing.CrossListQueryCache.GetSiteData(SPSite site, String webUrl) at Microsoft.SharePoint.Publishing.CrossListQueryCache.GetSiteData(SPSite site) at Microsoft.SharePoint.Publishing.WebControls.ContentByQueryWebPart.IssueQuery() at Microsoft.SharePoint.Publishing.WebControls.ContentByQueryWebPart.GetXPathNavigator(String viewPath) at Microsoft.SharePoint.WebPartPages.DataFormWebPart.PrepareAndPerformTransform() at Microsoft.SharePoint.WebPartPages.DataFormWebPart.PerformSelect() at Microsoft.SharePoint.WebPartPages.DataFormWebPart.DataBind() at Microsoft.SharePoint.WebPartPages.DataFormWebPart.EnsureDataBound() at Microsoft.SharePoint.WebPartPages.DataFormWebPart.CreateChildControls() at System.Web.UI.Control.EnsureChildControls() at System.Web.UI.Control.PreRenderRecursiveInternal() at System.Web.UI.WebControls.WebParts.WebPart.PreRenderRecursiveInternal() at System.Web.UI.Control.PreRenderRecursiveInternal() at System.Web.UI.Control.PreRenderRecursiveInternal() at System.Web.UI.Control.PreRenderRecursiveInternal() at System.Web.UI.Control.PreRenderRecursiveInternal() at System.Web.UI.Control.PreRenderRecursiveInternal() ..............

· While debugging through a custom webpart deriving from CQWP, following exception appears after a few seconds in the ‘ModifyXsltArgumentList’ function of CQWP.

An unhandled exception of type 'System.StackOverflowException' occurred in mscorlib.dll

· On applying any basic XSL template , which doesn’t remove HTML mark-up from the @Body field of the CQWP resultset , such as below :

<xsl:template name="ShowXML" match="Row[@Style='ShowXML']" mode="itemstyle">

    <xsl:for-each select="@*">

      <br />

      Name: <xsl:value-of select="name()" />

      <br />Value:<xsl:value-of select="." />

    </xsl:for-each>

  </xsl:template>

doesn’t result in any loop-back or crash.

Here’s the “item-style” xsl that can be used to trim the blog post’s body content. Here we have to use a “removemarkup” template\function that will remove the html markup as blog post’s body will be stored in its raw form in the content database.

  <xsl:template name="removeMarkup">

    <xsl:param name="string" />

    <xsl:choose>

      <xsl:when test="contains($string, '&lt;')">

        <xsl:variable name="nextString">

          <xsl:call-template name="removeMarkup">

            <xsl:with-param name="string" select="substring-after($string, '&gt;')" />

          </xsl:call-template>

        </xsl:variable>

        <xsl:value-of select="concat(substring-before($string, '&lt;'), $nextString)" />

      </xsl:when>

      <xsl:otherwise>

        <xsl:value-of select="$string" />

      </xsl:otherwise>

    </xsl:choose>

  </xsl:template>

 

  <xsl:template name="CQWP_Blog" match="Row[@Style='CQWP_Blog']" mode="itemstyle">

    <xsl:variable name="SafeLinkUrl">

      <xsl:call-template name="OuterTemplate.GetSafeLink">

        <xsl:with-param name="UrlColumnName" select="'LinkUrl'"/>

      </xsl:call-template>

    </xsl:variable>

    <xsl:variable name="SafeImageUrl">

      <xsl:call-template name="OuterTemplate.GetSafeStaticUrl">

        <xsl:with-param name="UrlColumnName" select="'ImageUrl'"/>

      </xsl:call-template>

    </xsl:variable>

    <xsl:variable name="DisplayTitle">

      <xsl:call-template name="OuterTemplate.GetTitle">

        <xsl:with-param name="Title" select="@Title"/>

        <xsl:with-param name="UrlColumnName" select="'LinkUrl'"/>

      </xsl:call-template>

    </xsl:variable>

    <xsl:variable name="LinkTarget">

      <xsl:if test="@OpenInNewWindow = 'True'" >_blank</xsl:if>

    </xsl:variable>

 

       <xsl:variable name="bodyContent">

      <xsl:call-template name="removeMarkup">

<!--<xsl:with-param name="string" select="$bodyContent" />-->

        <xsl:with-param name="string" select="substring(@Body,1,1000)" />

      </xsl:call-template>

    </xsl:variable>

 

   <div id="linkitem" class="item">

      <xsl:if test="string-length($SafeImageUrl) != 0">

        <div class="image-area-left">

          <a href="{$SafeLinkUrl}" target="{$LinkTarget}">

            <img class="image" src="{$SafeImageUrl}" alt="{@ImageUrlAltText}" />

          </a>

        </div>

      </xsl:if>

      <div class="link-item">

        <xsl:call-template name="OuterTemplate.CallPresenceStatusIconTemplate"/>

        <a href="{$SafeLinkUrl}" target="{$LinkTarget}" title="{@LinkToolTip}">

          <span class="ms-announcementtitle">

            <xsl:value-of select="$DisplayTitle"/>

          </span> (written <xsl:value-of select="@Created" /> by <xsl:value-of select="@Author" />)

        </a>

        <div class="description">

    <!--<xsl:value-of select="substring($bodyContent,1,1000)" disable-output-escaping="yes" />-->

          <xsl:value-of select="$bodyContent" disable-output-escaping="yes" />

          ...(<a href="{$SafeLinkUrl}" mce_href="{$SafeLinkUrl}" target="{$LinkTarget}" title="{@LinkToolTip}">more</a>)

          <br /><br />

        </div>

      </div>

    </div>

  </xsl:template>

Basically, we only want a substring (@Body, 1, 1000) in CQWP (w\o htmlmarkup) and then append "more..." to it, But the issue above is that we were first passing the whole "@Body" field value to the 'removemarkup' function (via bodyContent variable) , afterwards calling substring($bodyContent,1,1000) to take a part of it to display. and then appending "more..." to it, as in :

<xsl:value-of select="substring($bodyContent,1,1000)" disable-output-escaping="yes" />

We have to make sure we pass on a substring of body not only while displaying it (which is in div tag towards the end), but also to removemarkup function.

In summary ,if you are facing performance issues while using CQWP, which you have altered to use a custom itemstyle of your own, then check the ItemStyle for such things which are easy to miss but can lead to serious performance issue.