Published on January 18th, 2011.
In my previous post I started a simple tutorial of how to create your own custom connector for Business Data Connectivity Services for SharePoint 2010. I created my own project in CodePlex where to store the code of these posts. So in this article I will show you the basics of creating your own connector.
Custom connectors are the ultimate abstraction in BDC services. Basically you have the model definition as .NET objects and you can manipulate it as you wish. Internally you can call services, read files, access databases, etc. As a result you need to provide an object which conforms to method’s return type description in the model definition.
Here are the steps when creating a custom connector:
- Create your model definition
- Write code-behind
- Deploy the solution
Note, that the first two steps can be interchanged. Some people prefer to start by writing the code-behind, deploy it and then start wirting their models. However I think it’s a better approach if you start with the model. Having it done in advance you can concentrate on its interpretation. So in this post I will exaplain how to create your model definition.
As I wrote in my previous post, I will create a custom connector which communicates with a RESTful service. For the purpose of this article, I have created a WCF Data Service, which returns details about emplyees. Data is taken from our favorite database – AdventureWorks. I want to make it as generic as possible so I can use it with most of such services. So let’s start exploring the model definition. This definition is nothing more than a XML file with a special structure. It also has its own XML Schema. Here is the structure of this file:
A Model is a collection of LobSystems. It is identified by a name. So we are creating a model for a RESTful connector. Our model will contains only one LoB system.
<Model Name="REST Connector Model">
Each LobSystem represents an external system with specific business logic. In our case, this is a RESTful web service. Every element in the tree inherits from a common one. Thus, every element declares common attributes and subelements, like Properties. Properties provides a collection of properies, each with a name, type and value. Having that, you can attach custom metadata to every element in the tree and access it in code-behind. Each LobSystem is identified by a name and a type. Possible types are:
The names are meaningful themselves. In our case, we need to choose Custom. When you use Custom type, you must provide a special property, called SystemUtilityTypeName. Its value is the full-qualified class name, which will interpret the model definition. We will leave this for later, when we build that class.
<LobSystem Name="RestLOB" Type="Custom"> <Properties> <Property Name="SystemUtilityTypeName" Type="System.String">...</Property> </Properties>
It is possible that one model contains many LoB systems. Each LoB system has running instances. The difference between a LoB system and an instance is that the LoB system is something generic. While the instance contains specific information for connecting to the external system. For example, if we use a Database LoB system, the isntance must have a set of properties, which describe the connection string. In my example, I am creating a LoB system which will provide details about employees. So I can call my LoB system instance EmployeeRest. I will atach a property to my instance, which defines the base URL of my RESTful service.
<LobSystemInstances> <LobSystemInstance Name="EmployeeRest"> <Properties> <Property Name="ServiceUrl" Type="System.String"> http://adventureworks.com/EmployeeService.svc/ </Property> </Properties> </LobSystemInstance> </LobSystemInstances>
Now we have to declare our entities. An entity represents an external content type. It has a name and a namespace. The concept of namespaces is the same as in C#, for instance. An entity describes the structure of a business entity. Normally, it corresponds to an object from the real life – a customer, an order, etc. An entity has a collection of identifiers, which form its identity. An identifier has a name and a type. So, we will define only one entity, called Employee. It will have only one identifier – EmployeeID.
<Entities> <Entity Name="Employee" Namespace="AdventureWorks"> <Identifiers> <Identifier Name="EmployeeID" TypeName="System.Int32"/> </Identifiers>
An entity have a set of methods (at least one is required). Note, that the real structure of an entity is exposed by the return type of its methods. Said in another way, we do not define the entire structure of our business entities at one place. In the beginning this may sound weird to you, but actually this creates a big abstraction and a few problems, which I will warn you about later.
The second compound part of an entity are methods and method instances. Methods are non-executable elements, whereas MethodInstances represent executable instances of Methods. Each method has a set of parameters. Each parameter has a direction (in, in/out, return), a name and a collection of type descriptors. Type descriptors are used to describe a real type. Here is an example representation of our entity described with type descriptors:
<TypeDescriptor TypeName="..." Name="Employee"> <TypeDescriptors> <TypeDescriptor Name="EmployeeID" TypeName="System.Int32" IdentifierName="EmployeeID" ReadOnly="true"/> <TypeDescriptor Name="Title" TypeName="System.String" /> <TypeDescriptor Name="HireDate" TypeName="System.DateTime" /> </TypeDescriptors> </TypeDescriptor>
As you can see, using TypeDescriptors is quite straightforward. In order to declare complex types, you can use type descriptors as children of another type descriptors. The type name of the Employee descriptor must contain the full-qualified class name of our real Employee class.
Let’s go on to the method declaration. A method defines a collection of parameters as I already mentioned. Method instances are stereotyped to a pre-defined BDC operations. This list includes operations like:
- Finder – returns a collection of items
- SpecificFinder – return a single item
- Updater – updates an item
- Creator – creates an item
- Deleter – deletes an item
Using these pre-defined operations, BCS can abstract methods for every kind of an external system. Let’s create a method, which returns a list of employees. This method will have only one RETURN parameter – a collection!
<Method Name="ReadEmployees"> <Parameters> <Parameter Direction="Return" Name="employeeList"> <TypeDescriptor Name="EmployeeList" TypeName="System.Collections.Generic.IEnumerable`1[System.String]" IsCollection="true"> <TypeDescriptors> <TypeDescriptor TypeName="..." Name="Employee">
Note how we define the collection. You may notice that the type name of the collection is IEnumerable<string> but we actually define the items of that collection to be of type Employee. This is a hack so that the type name of the collection can be successfully parsed by the .NET runtime. In code-behind we construct a collection of employees, bot of strings. The data returned by our RESTful service is XML. We need a way to map between that data and the properties of the Employee type. Here comes XPath. With XPath expressions we can map each property to an element (or an attribute) of the returned XML. As I mentioned earlier, you can attach properties to every element in the model tree. That is what we will use to describe the XPath expressions. A type descriptor for a property will look like this:
<TypeDescriptor Name="Title" TypeName="System.String" <Properties> <Property Name="Expression" Type="System.String">tns:content/m:properties/d:Title</Property> </Properties> </TypeDescriptor>
We will read this expression in code-behind and execute it against the returned data. After writing such expression for all properties of our return type, we proceed to the MethodInstance declaration. We are creating a method, which returns a collection of employees, so it’s type must be Finder.
<MethodInstances> <MethodInstance Type="Finder" ReturnParameterName="employeeList" Name="GetEmployees"> <Properties> <Property Name="Url" Type="System.String">Employees</Property> <Property Name="ElementsExpression" Type="System.String">/tns:feed/tns:entry</Property> <Property Name="Namespaces" Type="System.String">tns|http://www.w3.org/2005/Atom m|http://schemas.microsoft.com/ado/2007/08/dataservices/metadata d|http://schemas.microsoft.com/ado/2007/08/dataservices</Property> </Properties> </MethodInstance> </MethodInstances>
We also must specify the name of the RETURN parameter. Again, we are using properties to define metadata. The Url is the URL of the method. We have already defined the base URL of the service, so the real URL will be the concatenation of those two. The ElementsExpression property uses XPath to set the path from the root of the returned XML to the employee element. Finally, the Namespaces property defines a collection of namespaces in the XML. These namespaces are used so that XPath can work properly.
Every Finder method must be accompanied by a SpecificFinder one. The definition of such is the same. A problem arises if you specify different type descriptors for the properties of the returned entity in both methods. For example, the SpecificFinder method does not declare a type descriptor for the property Title. In this case when you try to import the model in SharePoint you will receive one strange error which states nothing about this. So beware of that!
After completing the model we can proceed to the code-behind. In the following post I will show you how to read the model through SharePoint BCS API and implement the methods from it.
Filled under SharePoint.