Friday, March 5, 2010

Writing Custom Connector for BCS

BCS in SharePoint 2010 provides following connectors - Database, WebService, WCF, .NET and custom connector. This blog explains how you can write and deploy your own custom connector, if one of the BCS connectors do not meet your requirements.

This documentation (http://msdn.microsoft.com/en-us/library/ee554911(office.14).aspx) explains when to use .NET assembly connector and when to write your own custom connector.

Assuming that you are required to write your own custom connector, lets look at the steps to achieve the goal.

The steps for custom connectors are
  • Write code for custom connector
  • Deploy custom connector in SharePoint
  • Write model for custom connector
Writing Custom Connector

Writing custom connector requires implementing ISystemUtility (http://msdn.microsoft.com/en-us/library/microsoft.businessdata.runtime.isystemutility(office.14).aspx) interface. The simple most custom connector requires implementing ExecuteStatic method in the interface. Other methods/properties can be boiler plate code.

Here is the simple most custom connector. Although the connector does not do anything, technically it can be deployed in SharePoint 2010.

using System;
using System.Collections;
using System.Collections.Generic;

using Microsoft.BusinessData.MetadataModel;
using Microsoft.BusinessData.Runtime;
using Microsoft.BusinessData.Infrastructure;

namespace SharePointConnector
{
    public class Connector : ISystemUtility
    {

        #region ISystemUtility Members

        public IEnumerator CreateEntityInstanceDataEnumerator(object rawStream, 
            ISharedEntityState sharedEntityState)
        {
            // implement your enumerator
        }

        public IConnectionManager DefaultConnectionManager
        {
            get { return null; }
        }

        public ITypeReflector DefaultTypeReflector
        {
            get { return null; }
        }

        public void ExecuteStatic(IMethodInstance mi, 
            ILobSystemInstance si, 
            object[] args, 
            IExecutionContext context)
        {
            // implement your logic
        }

        #endregion
    }
}


Figure 1: Simple most custom connector

Let's take a look at the various methods and properties in the ISystemUtility interface.

CreateEntityInstanceDataEnumerator
This method converts line-of-business data stream into an enumerator of raw AdapterObjects. Implementing this method is necessary. If your connector gets IEnumerable as raw stream, you can just return the corresponding enumerator from the method.

DefaultConnectionManager 
This property allows you to manage connections for your external system. You can return null if you don't want connection management. BDC (Business Data Connectivity) will use its own default connection manager.

DefaultTypeReflector 
This property allows you manage the type reflection for your objects. For example, if your external system returns stream, BDC will unable to do a meaningful type reflection and you will have to write your own type reflection. If your external system returns .NET types than you don't need to provide any type reflection. For default implementation this property should return null.

ExecuteStatic
This method is the most important method in the interface. In the method, you should implement CRUDQ (create, read, update, delete and query) stereotypes for the connector.

public void ExecuteStatic(IMethodInstance mi, 
            ILobSystemInstance si, 
            object[] args, 
            IExecutionContext context)


Lets take a look at the method parameters

mi : This parameter represents the MethodInstance that is being executed by BDC. This parameter corresponds to the
<MethodInstance>
element with the LobSystem/Entity/Method in the BDC metadata model.

si : This parameter represents the LobSystemInstance the method instance is being executed against. This parameter corresponds to the
<LobSystemInstance>
element with the LobSystem in the BDC metadata model.

args : This parameter is the Parameters of the MethodInstance that is being executed. The last item in the array is reserved for the return parameter for the method instance.

context : This parameter sets the execution context of BDC. For all practical purposes this parameter can be ignored because External List in SharePoint does not set the execution context and you or BDC will not control this variable. The context can be different for BDC running under Office client.

Ok, now that we understand ISystemUtility interface, lets do some fun stuff.

Sample
In this sample, I will create a custom connector that will return "Movie" entity from an external system. For simplicity, the connector will support only "Finder" and "SpecificFinder" stereotypes. This sample will use in-memory data (external system).

Since the external system returns .NET types, implementation of the custom connector is simple.

public IEnumerator CreateEntityInstanceDataEnumerator(object rawStream, 
    ISharedEntityState sharedEntityState)
{
    IEnumerable enumerableStream = rawStream as IEnumerable;
    if (enumerableStream != null)
    {
        return enumerableStream.GetEnumerator();
    }

    throw new InvalidOperationException("not valid stream returned");
}
Figure 2: CreateEntityInstanceDataEnumerator implementation

In the method CreateEntityInstanceDataEnumerator, the base Enumerator is returned. Off course, for a real external system you may have to write an enumerator.

ExecuteStatic method will check what kind of stereotype is being executed and will execute corresponding methods on the external system. As said before, this connector just supports Finder/SpecificFinder stereotype, so it will throw for other stereotypes.

public void ExecuteStatic(IMethodInstance mi, 
    ILobSystemInstance si, 
    object[] args, 
    IExecutionContext context)
{
    // provide only read functionality
    switch(mi.MethodInstanceType)
    {
        case MethodInstanceType.SpecificFinder:

            IParameterCollection parameters = mi.GetMethod().GetParameters();

            // make sure there is only one input parameter for the method
            // and one return parameter.
            if (parameters.Count != 2 )
            {
                string message = "Method " + mi.GetMethod().Name +" must have one input and one return parameter";
                throw new InvalidMetadataObjectException(message);
            }

            // check if the input parameter is integer type
            // and the return parameter is "Movie" type
            Type param1Type = Type.GetType(parameters[0].GetRootTypeDescriptor().TypeName, false);
            Type param2Type = Type.GetType(parameters[1].GetRootTypeDescriptor().TypeName, false);

            if ( param1Type == null || param1Type != typeof(Int32))
            {
                string message = "Method " + mi.GetMethod().Name +" must contain input of type System.Int32";
                throw new InvalidMetadataObjectException(message);
            }

            if ( param2Type == null || param2Type != typeof(Movie))
            {
                string message = "Method " + mi.GetMethod().Name +" must contain input of type " + typeof(Movie).ToString();
                throw new InvalidMetadataObjectException(message);
            }

            int id = (int)args[0];
            args[1] = MovieData.GetMovie(id);

            break;

        case MethodInstanceType.Finder:
            args[args.Length-1] = MovieData.GetMovies();
            break;

        default:
            throw new NotImplementedException();
    }
}
Figure 3: ExecuteStatic implementation

The method verifies the Finder/SpecificFinder signature in metadata model. This way the connector can ensure that the metadata model is not invalid. Finally it executes the external system and sets the return value in args parameter.

I have attached complete source code at the end of this post.

Deploying Custom Connector in SharePoint

Deploying custom connector in SharePoint 2010 is pretty straight forward. All you need is to GAC the assembly in all  the SharePoint machines. This includes web-front ends as well as application servers in the farm. If you have the requirement to execute custom connectors on Office client ( take External List to Outlook or Workspaces ) you will need to GAC the assembly in client machines as well.

Writing Models for Custom Connector

Now that you have written the custom connector and have already deployed in the SharePoint, lets get a sample external list for the custom connector.

The LobSystem Type for custom connector must specify  "Custom". When custom connectors are used, the model must contain the SystemUtilityTypeName property for LobSystem.

<LobSystems>
    <LobSystem Type="Custom" Name="CustomLobSystem">
      <Properties>
        <Property Type="System.String" Name="SystemUtilityTypeName">SharePointConnector.Connector, SharePointConnector, Version=1.0.0.0, Culture=neutral, PublicKeyToken=dc97b363e814985e</Property>
      </Properties>

Figure 4: LobSystem properties for custom connector

SystemUtilityTypeName is the fully qualified name for custom connector type. The following figure shows snippet of the metadata model.

 
Figure 5: Metadata model snippet for custom connector

The sample metadata model is also attached in the source code (see end of post).

Custom connector in action

Now that we have our metadata model for custom connector, lets fire it up in the SharePoint. The following screen shots show the external list running against custom connector.

 
Figure 6: Choosing external content type



Figure 6 shows the external content type displayed for custom connector. At this point the custom connector is not being executed.




Figure 7: Finder stereotype executed in custom connector (via External list)

Figure 8: SpecificFinder stereotype executed in custom connector

Figure 7 and Figure 8 shows Finder and SpecificFinder stereotyped operations running in custom connector. Our custom connector executes the external system methods and returns appropriate data.

Source Code

Sample with complete source code can be downloaded from here.

As-is
The source code/software is provided "as-is". No claim of suitability, guarantee, or any warranty whatsoever is provided. Source Code and executable files can not be used in commercial applications.

9 comments:

Anonymous said...

Hi,

thank you for your Post. To deploy the custom bcs connector, I have copied the dll to the gac folder of the Sharepoint server C:\Windows\assembly But I still can not find it to create external content type. What are the steps to deploy the dll?
Regards
geerg

Jardalu LLC said...

You will need to gac the dll using gacutil tool. Gacutil tool description is here (http://msdn.microsoft.com/en-us/library/ex0ss12c(VS.80).aspx).

Anonymous said...

thanks for this write up! Helped clear up somethings..

Anonymous said...

Thank you for this great post, but how did i deploy the Metadata model in to sharepoint?

ADmin said...

Knowing the subject of your essay enables you to focus your efforts. You can immerse yourself in finding out all there is to know over here about a particular topic without any chance of getting distracted.

Arsalan Yousuf said...

Pleased to have find this web site. The data a person submitted is advantageous in many ways. The right path involving expressing on your own is laudable. Looking to studying much more. Will have fun below typically. It's a pleasure to read your post. are you struggling with your essays

a.danna said...

That's the kind of information I've been looking for. I didn't expect to find it for free though. I've also found some support about it on this writing services resource. Do you have any other suggestions?

Essay writing services said...

You have changed a very confusing code into the simple one..Thanks for this effort and i will be waiting for these kinds of updates in future..

Essay writing service

Unknown said...

It can be almost impossible to find well-qualified users on this matter, however, you look like you be aware of exactly what you’re covering!
sharepoint administrator training