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 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.
CreateEntityInstanceDataEnumeratorThis 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.
DefaultConnectionManagerThis 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.
DefaultTypeReflectorThis 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.
ExecuteStaticThis 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
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.