Finding the right item with FindItems

I have recently been developing against the SRS web service API, and have noticed a few peculiar things about its design. One in particular is the difference in behavior between ListChildren and FindItems.

Both of these methods take a parent path parameter (among others; see the links above for complete signatures) and return a CatalogItem[] collection. The difference is that ListChildren allows you to specify whether or not you want to search recursively. FindItems will only match items in (or presumably below, but I haven’t confirmed this) the path you pass to it. If you want to find children in the entire report server database, you pass "/" as the path, and it will return any item that matches your search criteria.

And there’s the rub. Consider the following:

  • You’re looking for a report named "foo" at the root
  • There is no such item, but there is a "foo" report under subfolder "bar" (and maybe other "foo" items throughout the catalog)

You’ll get an item (or more) returned, but because you’re searching the entire catalog, it won’t be the item you want. Drat!

Here’s some sample code that ensures you get the item you want, culled from a command-line utility I created to ease report deployment for our release management and operations teams:

using RptSvc = ReportingServices.ReportServer;
// ReportingServices.ReportServer class via VS.NET-generated Web Reference code

namespace RptSvcDeployer
{
  class Deployer
  {
    static RptSvc.CatalogItem GetItem( string ItemPath, RptSvc.ItemTypeEnum Type )
    {
      string Parent = ItemPath.Substring( 0, ItemPath.LastIndexOf("/") );
      string Item   = ItemPath.Remove( 0, Parent.Length + 1 );

      if ( 0 == Parent.Length ) Parent = "/";

      // create search condition for FindItems call
      RptSvc.SearchCondition srchCond = new RptSvc.SearchCondition();
      srchCond.Condition              = RptSvc.ConditionEnum.Equals;
      srchCond.ConditionSpecified     = true;
      srchCond.Name                   = "Name";
      srchCond.Value                  = Item;

      RptSvc.SearchCondition[] conditions = { srchCond };

      RptSvc.CatalogItem[] itemsFound = rs.FindItems( Parent, RptSvc.BooleanOperatorEnum.And, conditions );
      RptSvc.CatalogItem foundItem    = null;

      if ( null != itemsFound )
      {
        foreach ( RptSvc.CatalogItem ItemFound in itemsFound )
        {
          if ( Type == ItemFound.Type && ItemPath.ToLower() == ItemFound.Path.ToLower() )
          {
            foundItem = ItemFound;
            break;
          }
        }        
      }

      return foundItem;
    }
  }
}

 

The GetItem method takes a string path to the desired item (folder, report, data source, etc) in the report server database, as well as the item type from the ItemTypeEnum enumerator, makes the call to the web service, and then iterates through the CatalogItem[] collection returned, comparing the passed type and path of the desired item to the .Type and .Path properties of the current CatalogItem. It returns either an exact match or null.  

Including the item type in the call ensures that you don't end up with a datasource or folder named "foo" off the root when you're looking for a report.

Hope someone finds this useful!


This posting is provided "AS IS" with no warranties, and confers no rights.
Use of included script samples are subject to the terms specified at https://www.microsoft.com/info/cpyright.htm.