I’ve implemented a few classes to simplify an access to BDC meta data and values from external data sources. By means of these classes you can make request for values from external data source, using a BDC Entity Instance identifier(s) or a value of a certain BDC Entity field.
The root class BDCMetaRequest allows to interact with BDC as a meta data store. The main goal of the BDCMetaRequest is to get a meta descriptor of a certain external data type registered in BDC. The found descriptor is an object of the Entity class and can be accessed through the FoundEntity property.
A base abstract class BDCRequest derived from BDCMetaRequest contains some general members responsible for searching the external data item that meets criteria and for fetching any field value out from the found item. It’s supposed that the criteria and criteria-specific search implementation are encapsulated inside derived classes. The BDCRequest supplies the following important members:
-
FoundEntityInstance returns the external data item that meets criteria. In terms of BDC the found data item is a BDC Entity Instance;
-
GetBdcEntityInstance returns the found data item as DataTable with one row. The row represents either the record found in database or the object returned by a Web service;
-
GetBdcEntityInstanceFieldValue returns a value of a certain field. The field value is being extracted from the found data item;
*Note that all classes described in this post actively use the lazy loading, so the searching for external data item will be performed only if it hasn’t been found before.
As I mentioned above, the derived classes BDCRequestById and BDCRequestByValue are responsible for different search criteria and criteria-specific search implementation. The BDCRequestById accepts the identifier(s) of a sought-for BDC Entity Instance in the form of an encoded string or array of objects. While the BDCRequestByValue accepts a value, which will be applied to the first available BDC Entity filter (WildcardFilter or ComparisonFilter). BDCRequestByValue acts just as the user who, interacting with Picker Dialog, chooses the filter, types the required value in the proper text field and presses the Search button (see picture below).
Ok, below is the classes and a digram of them:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Portal.WebControls;
using Microsoft.Office.Server.ApplicationRegistry.Infrastructure;
using Microsoft.Office.Server.ApplicationRegistry.MetadataModel;
using Microsoft.Office.Server.ApplicationRegistry.Runtime;
using Microsoft.Office.Server;
using System.Data;
namespace BDC
{
/// <summary>
/// Abstracts an interaction with BDC meta data
/// </summary>
public class BDCMetaRequest
{
#region fields & properties
protected string _lobSystemInstanceName = null;
protected string _bdcEntityName = null;
protected LobSystemInstance _lobSystemInstance = null;
protected Entity _entity = null;
/// <summary>
/// Name of the requested Lob System Instance
/// </summary>
public string RequestedLobSystemInstanceName
{
get { return _lobSystemInstanceName; }
}
/// <summary>
/// Name of the requested external data type
/// </summary>
public string RequestedEntityName
{
get { return _bdcEntityName; }
}
/// <summary>
/// Found meta descriptor of external data type
/// </summary>
public virtual Entity FoundEntity
{
get { return _entity == null ? (_entity = GetEntity()) : _entity; }
}
/// <summary>
/// Found Lob System Instance
/// </summary>
public virtual LobSystemInstance FoundLobSystemInstance
{
get { return _lobSystemInstance == null ? (_lobSystemInstance = GetLobSystemInstance()) : _lobSystemInstance; }
}
#endregion
#region public methods
/// <summary>
/// Initializes a new instance of the BDCMetaRequest class
/// </summary>
/// <param name="lobSysInstanceName">Name of the requested Lob System Instance</param>
/// <param name="bdcEntityName">Name of the requested external data type</param>
public BDCMetaRequest(string lobSysInstanceName, string bdcEntityName)
{
// check if lobSysInstanceName isn't NULL or empty string
EnsureParamIsNotNullOrEmpty("lobSysInstanceName", lobSysInstanceName);
// check if bdcEntityName isn't NULL or empty string
EnsureParamIsNotNullOrEmpty("bdcEntityName", bdcEntityName);
_lobSystemInstanceName = lobSysInstanceName;
_bdcEntityName = bdcEntityName;
}
/// <summary>
/// Initializes a new instance of the BDCMetaRequest class
/// </summary>
/// <param name="businessDataField">SharePoint field of BusinessData type</param>
public BDCMetaRequest(BusinessDataField businessDataField)
{
// check if businessDataField isn't NULL
EnsureParamIsNotNull("businessDataField", businessDataField);
_lobSystemInstanceName = businessDataField.SystemInstanceName;
_bdcEntityName = businessDataField.EntityName;
}
#endregion
#region internal methods
/// <summary>
/// Gets the Entity object from BDC that meets criteria
/// </summary>
/// <returns>Found BDC Entity</returns>
protected Entity GetEntity()
{
// get BDC Entity from Lob System Instance by name
return FoundLobSystemInstance.GetEntities()[RequestedEntityName];
}
/// <summary>
/// Gets the LobSystemInstance from BDC that meets criteria
/// </summary>
/// <returns>Found LobSystemInstance</returns>
protected LobSystemInstance GetLobSystemInstance()
{
// get Lob System Instance by name
return ApplicationRegistry.GetLobSystemInstanceByName(RequestedLobSystemInstanceName);
}
/// <summary>
/// Throws an exception if the passed parameter value is NULL or empty string
/// </summary>
/// <param name="paramName">Parameter name</param>
/// <param name="value">Parameter value</param>
protected static void EnsureParamIsNotNullOrEmpty(string paramName, string value)
{
if (string.IsNullOrEmpty(value))
throw new ArgumentException(string.Format("{0} must not be NULL or empty!", paramName), paramName);
}
/// <summary>
/// Throws an exception if the passed parameter value is NULL
/// </summary>
/// <param name="paramName">Parameter name</param>
/// <param name="value">Parameter value</param>
protected static void EnsureParamIsNotNull(string paramName, object value)
{
if (value == null)
throw new ArgumentNullException(paramName, string.Format("{0} must not be NULL!", paramName));
}
#endregion
}
/// <summary>
/// Abstracts an interaction with BDC
/// </summary>
public abstract class BDCRequest : BDCMetaRequest
{
protected bool _formattedValues = false;
protected IEntityInstance _iEntityInstance = null;
/// <summary>
/// Indicates if result values will be formatted
/// </summary>
public bool FormattedValues
{
get { return _formattedValues; }
set { _formattedValues = value; }
}
/// <summary>
/// Gets found data item from external datasource
/// </summary>
public IEntityInstance FoundEntityInstance
{
get { return _iEntityInstance == null ? (_iEntityInstance = GetEntityInstance()) : _iEntityInstance; }
}
/// <summary>
/// Initializes a new instance of the BDCRequest class
/// </summary>
/// <param name="lobSysInstanceName">Name of the Lob System Instance</param>
/// <param name="bdcEntityName">Type name of returned Business Data Object</param>
/// <remarks>This constructor is called by derived class constructors</remarks>
public BDCRequest(string lobSysInstanceName, string bdcEntityName) : base(lobSysInstanceName, bdcEntityName)
{
}
/// <summary>
/// Initializes a new instance of the BDCRequest class
/// </summary>
/// <param name="businessDataField">SharePoint field of BusinessData type</param>
/// <remarks>This constructor is called by derived class constructors</remarks>
public BDCRequest(BusinessDataField businessDataField) : base(businessDataField)
{
}
/// <summary>
/// Returns a DataTable with one row, which represents either a record from a database or an object returned by a Web service
/// </summary>
/// <returns>DataTable with one row as the found data item from external data source</returns>
public virtual DataTable GetBdcEntityInstance()
{
IEntityInstance entityInstance = FoundEntityInstance;
return FormattedValues ? entityInstance.EntityAsFormattedDataTable : entityInstance.EntityAsDataTable;
}
/// <summary>
/// Returns a value of certain field of the data item found in external data source
/// </summary>
/// <param name="bdcEntityFieldName">Field name</param>
/// <returns>Field value</returns>
public virtual object GetBdcEntityInstanceFieldValue(string bdcEntityFieldName)
{
if (!string.IsNullOrEmpty(bdcEntityFieldName))
{
IEntityInstance entityInstance = FoundEntityInstance;
return GetFieldValue(entityInstance, bdcEntityFieldName);
}
return null;
}
/// <summary>
/// Gets BDC Entity Instance that meets criteria
/// </summary>
/// <returns>Found BDC Entity Instance</returns>
protected abstract IEntityInstance GetEntityInstance();
/// <summary>
/// Gets a value of certain field of BDC Entity Instance
/// </summary>
/// <param name="entityInstance">BDC Entity Instance</param>
/// <param name="bdcEntityFieldName">Field name</param>
/// <returns>Field value</returns>
protected virtual object GetFieldValue(IEntityInstance entityInstance, string bdcEntityFieldName)
{
foreach (Field field in entityInstance.ViewDefinition.Fields)
if (bdcEntityFieldName.Equals(field.Name, StringComparison.OrdinalIgnoreCase) ||
bdcEntityFieldName.Equals(field.DefaultDisplayName, StringComparison.OrdinalIgnoreCase) ||
(field.ContainsLocalizedDisplayName && bdcEntityFieldName.Equals(field.LocalizedDisplayName, StringComparison.OrdinalIgnoreCase)))
return FormattedValues ? entityInstance.GetFormatted(field.Name) : entityInstance[field.Name];
return null;
}
}
/// <summary>
/// Represents search of data item in external data source, using identifier(s)
/// </summary>
public class BDCRequestById : BDCRequest
{
protected string _bdcEntityInstanceEncodedId = null;
protected object[] _bdcEntityInstanceIds = null;
/// <summary>
/// Encoded identifier(s) of data item
/// </summary>
public string BdcEntityInstanceEncodedId
{
get { return _bdcEntityInstanceEncodedId; }
}
/// <summary>
/// Array containing identifier(s) of data item
/// </summary>
public object[] BdcEntityInstanceIds
{
get { return _bdcEntityInstanceIds; }
}
/// <summary>
/// Initializes a new instance of the BDCRequestById class
/// </summary>
/// <param name="lobSysInstanceName">Name of the Lob System Instance</param>
/// <param name="bdcEntityName">Type name of returned Business Data Object</param>
/// <param name="bdcEntityInstanceEncId">Encoded identifier(s) of data item</param>
public BDCRequestById(string lobSysInstanceName, string bdcEntityName, string bdcEntityInstanceEncId)
: base(lobSysInstanceName, bdcEntityName)
{
// check if bdcEntityInstanceEncId is a valid encoded identifier(s)
EnsureParamIsEncodedIdentifier("bdcEntityInstanceEncId", bdcEntityInstanceEncId);
_bdcEntityInstanceEncodedId = bdcEntityInstanceEncId;
// get decoded version of passed encoded identifier(s)
_bdcEntityInstanceIds = EntityInstanceIdEncoder.DecodeEntityInstanceId(_bdcEntityInstanceEncodedId);
}
/// <summary>
/// Initializes a new instance of the BDCRequestById class
/// </summary>
/// <param name="businessDataField">SharePoint field of BusinessData type</param>
/// <param name="bdcEntityInstanceEncId">Encoded identifier(s) of data item</param>
public BDCRequestById(BusinessDataField businessDataField, string bdcEntityInstanceEncId)
: base(businessDataField)
{
// check if bdcEntityInstanceEncId is a valid encoded identifier(s)
EnsureParamIsEncodedIdentifier("bdcEntityInstanceEncId", bdcEntityInstanceEncId);
_bdcEntityInstanceEncodedId = bdcEntityInstanceEncId;
// get decoded version of passed encoded identifier(s)
_bdcEntityInstanceIds = EntityInstanceIdEncoder.DecodeEntityInstanceId(_bdcEntityInstanceEncodedId);
}
/// <summary>
/// Initializes a new instance of the BDCRequestById class
/// </summary>
/// <param name="lobSysInstanceName">Name of the Lob System Instance</param>
/// <param name="bdcEntityName">Type name of returned Business Data Object</param>
/// <param name="bdcEntityInstanceIds">Array containing identifier(s) of data item</param>
public BDCRequestById(string lobSysInstanceName, string bdcEntityName, object[] bdcEntityInstanceIds)
: base(lobSysInstanceName, bdcEntityName)
{
// check if array bdcEntityInstanceIds isn't NULL and empty
EnsureArrayIsNotNullOrEmpty("bdcEntityInstanceIds", bdcEntityInstanceIds);
_bdcEntityInstanceIds = bdcEntityInstanceIds;
// get encoded version of passed identifier(s)
_bdcEntityInstanceEncodedId = EntityInstanceIdEncoder.EncodeEntityInstanceId(bdcEntityInstanceIds);
}
/// <summary>
/// Initializes a new instance of the BDCRequestById class
/// </summary>
/// <param name="businessDataField">SharePoint field of BusinessData type</param>
/// <param name="bdcEntityInstanceIds">Array containing identifier(s) of data item</param>
public BDCRequestById(BusinessDataField businessDataField, object[] bdcEntityInstanceIds)
: base(businessDataField)
{
// check if array bdcEntityInstanceIds isn't NULL and empty
EnsureArrayIsNotNullOrEmpty("bdcEntityInstanceIds", bdcEntityInstanceIds);
_bdcEntityInstanceIds = bdcEntityInstanceIds;
// get encoded version of passed identifier(s)
_bdcEntityInstanceEncodedId = EntityInstanceIdEncoder.EncodeEntityInstanceId(bdcEntityInstanceIds);
}
/// <summary>
/// Throws an exception if the passed parameter value is a valid encoded identifier(s)
/// </summary>
/// <param name="paramName">Parameter name</param>
/// <param name="value">Parameter value</param>
public static void EnsureParamIsEncodedIdentifier(string paramName, string value)
{
if (string.IsNullOrEmpty(value) || !EntityInstanceIdEncoder.IsEncodedIdentifier(value))
throw new ArgumentException("Invalid encoded identifier!", paramName);
}
/// <summary>
/// Throws an exception if the passed parameter value isn't NULL and empty array
/// </summary>
/// <param name="paramName">Parameter name</param>
/// <param name="value">Parameter value</param>
public static void EnsureArrayIsNotNullOrEmpty(string paramName, object[] value)
{
if (value == null || value.Length == 0)
throw new ArgumentException("Array must not be NULL or empty!", paramName);
}
/// <summary>
/// Gets BDC Entity Instance that meets identifier(s)
/// </summary>
/// <returns>Found BDC Entity Instance</returns>
protected override IEntityInstance GetEntityInstance()
{
// get BDC Entity Instance by its identifier(s)
return FoundEntity.FindSpecific(BdcEntityInstanceIds, FoundLobSystemInstance);
}
}
/// <summary>
/// Represents search of data item in external data source by basing on a certain field value.
/// The value has to belong to the field used in item picker in UI
/// </summary>
public class BDCRequestByValue : BDCRequest
{
protected string _value = null;
/// <summary>
/// Field value to search data item by
/// </summary>
public string BdcEntityInstanceFieldValue
{
get { return _value; }
}
/// <summary>
/// Initializes a new instance of the BDCRequestByValue class
/// </summary>
/// <param name="lobSysInstanceName">Name of the Lob System Instance</param>
/// <param name="bdcEntityName">Type name of returned Business Data Object</param>
/// <param name="bdcEntityInstanceFieldValue">Field value to search data item by</param>
public BDCRequestByValue(string lobSysInstanceName, string bdcEntityName, string bdcEntityInstanceFieldValue)
: base(lobSysInstanceName, bdcEntityName)
{
// check if bdcEntityInstanceFieldValue isn't NULL and empty string
EnsureParamIsNotNullOrEmpty("bdcEntityInstanceFieldValue", bdcEntityInstanceFieldValue);
_value = bdcEntityInstanceFieldValue;
}
/// <summary>
/// Initializes a new instance of the BDCRequestByValue class
/// </summary>
/// <param name="businessDataField">SharePoint field of BusinessData type</param>
/// <param name="bdcEntityInstanceFieldValue">Field value to search data item by</param>
public BDCRequestByValue(BusinessDataField businessDataField, string bdcEntityInstanceFieldValue)
: base(businessDataField)
{
// check if bdcEntityInstanceFieldValue isn't NULL and empty string
EnsureParamIsNotNullOrEmpty("bdcEntityInstanceFieldValue", bdcEntityInstanceFieldValue);
_value = bdcEntityInstanceFieldValue;
}
/// <summary>
/// Gets BDC Entity Instance that meets the field value
/// </summary>
/// <returns>Found BDC Entity Instance</returns>
protected override IEntityInstance GetEntityInstance()
{
// search the entity using the first filter of available ones
FilterCollection fc = FoundEntity.GetFinderFilters();
if (fc[0] is WildcardFilter)
((WildcardFilter)fc[0]).Value = BdcEntityInstanceFieldValue; //"%" + request.BdcEntityInstanceFieldValue + "%";
else if (fc[0] is ComparisonFilter)
((ComparisonFilter)fc[0]).Value = BdcEntityInstanceFieldValue;
// find suitable BDC Entity instances and take the first one
IEntityInstanceEnumerator entityInstanceEnumerator = FoundEntity.FindFiltered(fc, FoundLobSystemInstance);
entityInstanceEnumerator.MoveNext();
return entityInstanceEnumerator.Current;
}
}
}
There is an ability to get formatted values by setting FormattedValues = true, however, complex formatting slows performance, so use it only if really necessary.
How to use the classes:
BusinessDataField businessDataField = null;
// usage of BDCRequestByValue
BDCRequestByValue requestByValue = new BDCRequestByValue("ExternalProductDB_Instance", "Products", "Microsoft Office");
DataTable theFirstFoundRecord = requestByValue.GetBdcEntityInstance();
object producer = theFirstFoundRecord.Rows[0]["Producer"];
object price = theFirstFoundRecord.Rows[0]["Price"];
object fieldValue = requestByValue.GetBdcEntityInstanceFieldValue("Producer");
requestByValue = new BDCRequestByValue(businessDataField, "Microsoft Office") { FormattedValues = true };
theFirstFoundRecord = requestByValue.GetBdcEntityInstance();
fieldValue = requestByValue.GetBdcEntityInstanceFieldValue("Producer");
// usage of BDCRequestById
BDCRequestById requestById = new BDCRequestById("ExternalProductDB_Instance", "Products", "__dk410035008400140025005400k410...");
theFirstFoundRecord = requestById.GetBdcEntityInstance();
producer = theFirstFoundRecord.Rows[0]["Producer"];
price = theFirstFoundRecord.Rows[0]["Price"];
fieldValue = requestById.GetBdcEntityInstanceFieldValue("Producer");
requestById = new BDCRequestById("ExternalProductDB_Instance", "Products", new object[] { 2 });
theFirstFoundRecord = requestById.GetBdcEntityInstance();
requestById = new BDCRequestById(businessDataField, new object[] { 2 });
theFirstFoundRecord = requestById.GetBdcEntityInstance();