Creating Custom Business Connectivity Services Connectors Using SharePoint Server 2010

Summary:  Learn how to create a Microsoft Business Connectivity Services (BCS) custom connector to integrate a variety of data sources into Microsoft SharePoint Server 2010.

Applies to: Business Connectivity Services | Office 2010 | Open XML | SharePoint Designer 2010 | SharePoint Foundation 2010 | SharePoint Online | SharePoint Server 2010 | Visual Studio

Provided by:  Ricardo Loo Foronda, Microsoft Corporation

Contents

  • Overview of Creating Business Connectivity Services Custom Connectors

  • Coding the Assembly

  • Creating the Model

  • Using the Custom Connector

  • Conclusion

  • Additional Resources

Click to get code  Download code: Custom BCS Connector Sample

Overview of Creating Business Connectivity Services Custom Connectors

Microsoft SharePoint Server 2010 offers integration with external systems (also known as line-of-business systems) through Microsoft Business Connectivity Services (BCS). However, some external systems require more flexibility and powerful mechanisms to correctly consume the data. Business Connectivity Services custom connectors offer you this flexibility and power.

This article addresses the main steps to help you create a Business Connectivity Services custom connector, as follows:

  • Coding the assembly

  • Creating the model

  • Using the custom connector

The example used in this article helps you construct a custom connector that interacts with the file system. Basically, it provides interaction with a specified folder through the following method types:

  • Finder  Returns a list of files that match the wildcard criteria that is specified.

  • SpecificFinder  Returns file information for the file name that is specified.

  • StreamAccessor  Returns a read-only file stream for the file name that is specified.

Coding the Assembly

A custom connector offers deep integration with Business Connectivity Services, providing mechanisms that manage connections, use different type systems, configure connections, and validate metadata to ensure that a common framework is used. You can build a custom connector to provide this functionality by implementing the following interfaces:

  • ISystemUtility  Required. This main interface of a connector provides mechanisms to execute operations against an external system.

  • IConnectionManager  Optional. This interface enables opening and closing connections to the external system.

  • ITypeReflector  Optional. This interface enables working with the data structures of the external system.

  • ISystemPropertyValidator  Optional. This interface verifies the metadata properties on an LobSystem element (external system) and on LobsystemInstance (external system instance) elements.

  • IAdministrableSystem  This interface can be implemented to provide the list of properties that can be configured through the Business Data Connectivity (BDC) service Administration user interface.

  • ISystemProxyGenerator  This interface compiles proxies to connect to the external system that can be persisted for performance.

Figure 1 shows an overview of the FileSystemConnector class.

Figure 1. FileSystemConnector overview

FileSystemConnector overview

Adding References and Implementing Interfaces

To begin to create a custom connector, you have to add references to the following assemblies:

  • Microsoft.BusinessData

  • Microsoft.SharePoint

You can locate the assembly files in the path c:\program files\common files\microsoft shared\web server extensions\14\isapi.

Next, you implement the following interfaces for the example to work:

  • ISystemUtility

  • IAdministrableSystem

  • ISystemPropertyValidator

The following sections describe these interfaces in more detail.

ISystemUtility Interface

As the main interface, ISystemUtility provides functionality to execute operations against the external system.

In our example, the ExecuteStatic method provides the functionality for the Finder method, SpecificFinder method, and StreamAccessor method. The following code shows the signature for the ExecuteStatic method.

public void ExecuteStatic(IMethodInstance methodInstance, ILobSystemInstance lobSystemInstance, object[] 
methodSignatureArgs, IExecutionContext context)

You should validate the in-parameters and the base folder, where Create, Read, Update, and Delete (CRUD) operations will occur. This is shown in the following code.

if (methodInstance == null)
{
    throw (new ArgumentNullException("methodInstance"));
}
if (lobSystemInstance == null)
{
    throw (new ArgumentNullException("lobSystemInstance"));
}
if (methodSignatureArgs == null)
{
    throw (new ArgumentNullException("args"));
}

object baseFolderValue;
if (!lobSystemInstance.GetProperties().TryGetValue(baseFolderPropertyName, out baseFolderValue))
{
    throw new InvalidOperationException("BaseFolder property is missing");
}

String baseFolderName = baseFolderValue as string;

if (String.IsNullOrEmpty(baseFolderName))
{
    throw new InvalidOperationException("BaseFolder property contains an invalid value.");
}

DirectoryInfo baseFolder = new DirectoryInfo(baseFolderName);

if (!baseFolder.Exists)
{
    throw new InvalidOperationException("Base folder doesn't exist.");
}

Now, notice the following:

  • The first parameter, IMethodInstance methodInstance, includes the MethodInstanceType property. This indicates the method type that is being called.

  • The third parameter, object[] methodSignatureArgs, can hold many subparameters that are useful for the MethodInstanceType property.

With this information, you can build a handler for every MethodInstanceType.

The Finder type has the following subparameters:

  • methodSignatureArgs[0]: A wildcarded search string for a file name.

  • methodSignatureArgs[1]: The return object (a list of FileInfo objects).

The Finder type subparameters are shown in the following code.

if (methodInstance.MethodInstanceType == MethodInstanceType.Finder)
{
    String wildcard = methodSignatureArgs[0] as string;
    IList<FileInfo> results = new List<FileInfo>();
    methodSignatureArgs[1] = baseFolder.GetFiles(wildcard);
}

The SpecificFinder type has the following subparameters:

  • methodSignatureArgs[0]: The file name.

  • methodSignatureArgs[1]: The return object (a FileInfo object).

The SpecificFinder type subparameters are shown in the following code.

if (methodInstance.MethodInstanceType == MethodInstanceType.SpecificFinder)
{
    string fileName = methodSignatureArgs[0] as string;
    FileInfo result = new FileInfo(Path.Combine(baseFolder.FullName, fileName));
    if (result.Exists && result.Directory.FullName.Equals(baseFolder.FullName))
    {
        methodSignatureArgs[1] = result;
    }
}

The StreamAccessor type has the following subparameters:

  • methodSignatureArgs[0]: The file name.

  • methodSignatureArgs[1]: The return object (a FileStream object).

The StreamAccessor type subparameters are shown in the following code.

if (methodInstance.MethodInstanceType == MethodInstanceType.StreamAccessor)
{
    string fileName = methodSignatureArgs[0] as string;
    FileInfo result = new FileInfo(Path.Combine(baseFolder.FullName, fileName));
    if (result.Exists && result.Directory.FullName.Equals(baseFolder.FullName))
    {
        methodSignatureArgs[1] = result.OpenRead();
    }
}

IAdministrableSystem Interface

As described earlier in this article, this IAdministrableSystem provides the list of properties that are administered through the Business Connectivity Services Administration UI. In this case, you have only one property, BaseFolder, which specifies your working directory.

You store the value in a class variable named baseFolderPropertyName through the AdministrableLobSystemInstanceProperties property, as shown in the following code.

public IList<AdministrableProperty> AdministrableLobSystemInstanceProperties
{
    get
    {
        return new List<AdministrableProperty>()
        {
            new AdministrableProperty("Base Folder","The path to the folder that stores the data.", 
              typeof(string), baseFolderPropertyName, typeof(string), true)  
        };
    }
}

ISystemPropertyValidator Interface

You use the ISystemPropertyValidator interface to verify the validity of the properties. In this case, you have only the BaseFolder property. You check whether the folder exists, and if it does not, you throw an exception. This is shown in the following code.

public void ValidateLobSystemInstanceProperty(string name, ref object value, string metadataObjectName, 
   Type metadataObjectType)
{
    // Validate whether the given directory exists.
    if (name == baseFolderPropertyName)
    {
        String folderName = value as string;
        if (!Directory.Exists(folderName))
        {
            throw new ArgumentException("The given directory does not exist.");
        }
    }
}

Creating the Model

You can create the model file and import it into the Business Data Catalog by using SharePoint Central Administration. To begin, you should have a general understanding of FileSystemLOB, as shown in Figure 2.

Figure 2. FileSystemLOB overview

FileSystemLOB overview

The following sections describe these properties, methods, and identifier.

FileSystemLOB Properties

The LobSystem element has the SystemUtilityTypeName property, which is defined by the following XML fragment. Notice that this matches the AdministrableLobSystemProperties list in the IAdministrableSystem interface.

<Properties>
  <Property Name="SystemUtilityTypeName" Type="System.String">
    BusinessDataConnectivitySamples.Connectors.FileSystemConnector,
    BusinessDataConnectivitySamples.Connectors,
    Version=14.0.0.0,
    Culture=Neutral,
    PublicKeyToken=2154d1c691e2e909
  </Property>
</Properties>

You put the fully qualified assembly name in the SystemUtilityTypeName property.

TestFileSystemCustomShimInstance Instance

The TestFileSystemCustomShimInstance instance is defined through the following XML fragment. Notice that the BaseFolder property matches the one in the IAdministrableSystem interface.

<LobSystemInstance Name="TestFileSystemCustomShim">
  <Properties>
    <Property Name="BaseFolder" Type="System.String">c:\testfolder</Property>
  </Properties>
</LobSystemInstance>

File External Content Type

The Entity XML element is the largest section of the model and defines the properties, methods, and identifier of the files. This is shown in the following XML fragment.

<Entity EstimatedInstanceCount="1000" Name="File" Namespace="BDCSamples.Connectors.FileSystem" Version="1.0.0.0">
<Identifiers>
  <Identifier TypeName="System.String" Name="FileName"/>
</Identifiers>
<Methods>
<Method Name="ReadFile">
<Method Name="ReadFiles">
<Method Name="GetFileContents">
</Methods>
</Entity>

Business Connectivity Services also needs a way to uniquely identify each element (in this case, the file). This is done by the identifier, which in this example is the FileName property.

Next are the methods, which in general adjust to the following structure:

  • In parameters

  • Return parameters

  • Method instances

  • Access control lists (ACLs)

The methods are mapped to the operations described in the ExecuteInternal method of the ISystemUtility interface. In the following markup, only the XML fragment for the ReadFile method is shown. But you can check the remaining operations in the files that accompany this article (see Custom BCS Connector Sample).

<Method Name="ReadFile">
  <Parameters>
  <Parameter Direction="In" Name="FileName">
    <TypeDescriptor TypeName="System.String" IdentifierName="FileName" Name="FileName" />
  </Parameter>
  <Parameter Direction="Return" Name="FileDetails">
    <TypeDescriptor TypeName="System.IO.FileInfo, mscorlib, Version=2.0.0.0, Culture=neutral, 
       PublicKeyToken=b77a5c561934e089" Name="FileDetails">
    <TypeDescriptors>
      <TypeDescriptor Name="Name" TypeName="System.String" IdentifierName="FileName" ReadOnly="true" />
      <TypeDescriptor Name="Attributes" TypeName="System.IO.FileAttributes, mscorlib, Version=2.0.0.0, 
        Culture=neutral, PublicKeyToken=b77a5c561934e089" ReadOnly="true"/>
      <TypeDescriptor Name="LastWriteTimeUtc" TypeName="System.DateTime" ReadOnly="true">
      <Interpretation>
        <NormalizeDateTime LobDateTimeMode="UTC"/>
      </Interpretation>
      </TypeDescriptor>
    </TypeDescriptors>
    </TypeDescriptor>
  </Parameter>
  </Parameters>
  <MethodInstances>
  <MethodInstance Type="SpecificFinder" ReturnParameterName="FileDetails" Name="FileSystemFinder">
    <!--<AccessControlList>
    <AccessControlEntry Principal="Domain\username">
      <Right BdcRight="Execute"/>
    </AccessControlEntry>
    </AccessControlList>-->
  </MethodInstance>
  </MethodInstances>
</Method>

Using the Custom Connector

The next step is to see the custom connector at work. Before you can do this, you will perform the following series of steps:

  1. Install the assembly in the global assembly cache.

  2. Import the model.

  3. Create the external list.

  4. Add the files to and remove them from the specified folder.

Installing the Assembly in the Global Assembly Cache

You can install the assembly in the global assembly cache in a few ways, but for the example in this article, simply drag the assembly file to the c:\windows\assembly folder.

Importing the Model

Next, use the following steps to import the model from the Central Administration pages.

To import the model from the Central Administration pages

  1. Open SharePoint 2010 Central Administration.

  2. Under Application Management, select Manage service applications.

  3. Click the Business Data Connectivity service application.

  4. Click the Edit tab, and then click Import on the Server ribbon.

  5. In the BDC Model File, select your model file (in this example, FileSystemConnector.bdcm).

  6. In File Type, verify that the model option is selected.

  7. In Advanced Settings, verify that Localized names, Properties, and Permissions are selected.

  8. Click Import.

Now, the model is imported, as shown in Figure 3.

Figure 3. Imported model

Imported model

Click on the model to discover the external content types, as shown in Figure 4.

Figure 4. External content types

External content types

Creating the External List

There are many ways to create the external list. However, this article describes only one way, as shown in the following steps.

To create the external list

  1. Navigate to the site where you want to create the external list.

  2. On Site Actions menu, click View All Site Content.

  3. Click Create.

  4. Select the External List type, and then click Create.

  5. Name your list, and then click Select External Content Type.

  6. Click Create.

Your external list is now ready to receive information (or files), as shown in Figure 5.

Figure 5. Created external list

Created external list

Adding or Removing Files

You add files to or remove files from the c:\testfolder folder, and the list synchronizes accordingly. Figure 6 shows this list and folder synchronization.

Figure 6. List and folder synchronization

List and directory synchronization

Conclusion

By using Microsoft Business Connectivity Services (BCS), you can connect to many different kinds of external systems. Each kind of external system requires a specific connector. By default, Business Connectivity Services includes connectors to access business data that is exposed through databases and web services. However, some business data might be exposed through other mechanisms, or might require additional processing before Business Connectivity Services can use the data. To solve the simple problems for one external system, Business Connectivity Services also provides the Microsoft .NET Framework assembly connector, where the logic to connect to the external system and any processing is compiled in a simple assembly that exposes functions that Business Connectivity Services can understand. While this set of connectors addresses most of the external systems, they are not always enough. You might need more control on the connections and models, or might have to connect to many different external systems of the same type. These problems are not solved by the .NET Framework assembly connector. However, as described in this article, you can build your own custom connector to solve them.

Additional Resources

For more information, see the following resources: