Expression Evaluation Time: ms / Round trip: ms
Part 2: Custom Data Aggregation in SharePoint with MatchPoint Data Providers
20. Dec
2011
by Stefan Category:
Technical
0

In part one of the series Custom Data Aggregation in SharePoint with MatchPoint Data Providers I showed you how to implement a basic data provider.

In this second part I'd like to explain how the data binding in the Data Grid Web Part is done. Data binding describes the link between the data source (your custom data provider) and the data view (the Data Grid Web Part). In other words, how MatchPoint extracts the value corresponding to the DataField specified in the column configuration.

When implementing a custom data provider you have to override the method IEnumerable<object> GetInteralData(). Each object in the collection represents a row in the result set.
Let's say you have a SimpleColumn configuration with "Title" as DataField. When extracting the value from the row, MatchPoint ...

  1. ... checks if there is a public field or property named "Title".
  2. ... checks if the row is of type IResultRecord and uses it's indexed property (resultRecord["Title"]) to return the value.
  3. ... uses the Expression Engine as fallback by executing the expression DataItem.Title on the row.

Important to note here is the order of the extraction. For example if there is already a public property named "Title" MatchPoint won't extract the value via the indexed property.

As you can see you can basically return any type in your custom data provider and the Data Grid Web Part is able tho extract the column values. You could for instance return a collection of SharePoint list items and the SPListItem's properties and field values will be available in the Data Grid Web Part.

Enough theory, let's write some code.

This question is about how to return a filtered list of sub-webs in an Expression Data Provider. There are two "problems" with this approach:

  1. The SPWeb objects returned by SPWeb.Webs have to be disposed. This cannot be achieved in a MatchPoint expression. Not disposing these objects could lead to a potential memory leak.
  2. The MatchPoint expression engine does not support object-to-LINQ style filtering in the current version (3.0).

So what do we do? We write our custom Web Data Provider!

We start with the configuration class for the data provider. Since we want to filter the webs by title we need to specify an expression where the filter value comes from. If this expression points to a TextField via the connection framework, we can build a find-on-type behavior. Additionally, it might be useful to let the user specify the parent web url:

  [Serializable]
  public class WebDataProvider : BaseDataProvider
  {
    [MemberDescriptor("Specifies an expression that returns a value which should be used to filter the web by title.")]
    public ExpressionString TitleFilterExpression;

    [MemberDescriptor("Specifies the URL of the parent web. If left empty, the current web will be used as parent.")]
    [SPUrlEditorBehavior(SPUrlType.Web)]
    [CustomEditor(typeof(SPUrlEditor))]
    public string ParentWebUrl;

    // ...
  }

The attributes [CustomEditor(typeof(SPUrlEditor))] and [SPUrlEditorBehavior(SPUrlType.Web)] tell the configuation editor to display an URL picker control:

Next comes the data provider instance. We need to override the method GetInternalData() and return our webs there. Since we have to dispose the web objects returned by SPWeb.Webs and we don't want to return a disposed instance we create a helper class that holds the required web information:

public class WebInfo: IResultRecord
{
  public readonly string Title;
  public readonly string Url;
  private IDictionary allProperties;

  public object this[string name]
  {
    get { return allProperties[name]; }
  }

  public WebInfo(string title, string url, IDictionary allProperties)
  {
    Title = title;
    Url = url;
    allProperties = allProperties;
  }
}

Since we do implement the Colygon.MatchPoint.Core.DataProviders.IResultRecord interface we enable one to create a data grid column that displays properties of a web.

In the next code block we retrieve the webs with correct disposing handling. If there is a filter expression set we apply a "starts-with filter" on the web title with the filter value.

private IEnumerable<WebInfo> GetWebs()
{
  // SPHelper.OpenWeb() will open the current web 
  // if provider.ParentWebUrl is null or empty.
  using (SPWeb parent = SPHelper.OpenWeb(provider.ParentWebUrl))
  {
    foreach (SPWeb child in parent.Webs)
    {
      // We do not return the SPWeb object here since 
      // we have to dispose elements of the SPWebCollection (SPWeb.Webs)
      using (child)
      {
        yield return new WebInfo(child.Title, child.Url, child.AllProperties);
      }
    }
  }
}

protected override IEnumerable<object> GetInternalData()
{
  IEnumerable<WebInfo> webs = GetWebs();
  // If there is no filter expression defined just return the webs.
  if (provider.TitleFilterExpression == null) return webs.ToArray();

  // Evaluate the filter expression to string and filter the webs 
  // by it's title.
  string filter = provider.TitleFilterExpression.EvaluateToString(null);
  return webs
    .Where(wi => wi.Title.StartsWith(filter, StringComparison.CurrentCultureIgnoreCase))
    .ToArray();
}

In order to support find-on-type behavior for our data provider we need to tell the connection framework that we are a data consumer. For this purpose we override the AddConnectionDependencies method on the data provider instance and add the TitleFilterExpression to the DependencyCollection.

public override void AddConnectionDependencies(DependencyCollection dependencies)
{
  base.AddConnectionDependencies(dependencies);
  dependencies.AddFromExpression(provider.TitleFilterExpression);
}

By registering our data provider as consumer the data grid will be automatically refreshed if the connected control changes.

Source Code

In the next posts of the series I will cover more scenarios for custom MatchPoint data providers such as:

  • Basics
  • Advanced configuration possiblities
  • Data binding
  • Caching
  • Paging
  • Support for MatchPoint conditions
  • Support for context menus in Data Grid Web Parts
  • Column name suggestion
Comments
ABOUT

This blog is about technical and non-technical aspects of the product MatchPoint and other SharePoint topics.

If you would like to post an article or if you have an idea for a post, please contact us.

ARCHIVE
COMMENTS
Matthias Weibel
09.04.2018 01:12
Link is updated and works now. | Goto Post
Dhanabalan
09.04.2018 12:21
Link doesn't work. Could anyone explain what does... | Goto Post
matthiaszbrun
14.03.2018 02:05
Hi Markus
We I use the config for SiteCollectionSe... | Goto Post
Reto Jeger
04.10.2017 09:15
Hello Reiner,
Thanks for pointing out the missing ... | Goto Post
rganser
29.09.2017 09:56
Hi, I downloaded the ZIP-file for MatchPoint Versi... | Goto Post