Implementing Metadata Browse Handler

Most databases and LOB applications provide some form of metadata that can be used to define the data that is exchanged between the adapter and the target system. Adapter Developer needs to implement IMetadataBrowseHandler interface provided within WCF LOB Adapter SDK for providing Adapter Consumer an ability to explore the available LOB application metadata. For example, a database provides a way to retrieve stored procedures, so a database adapter can provide the browse functionality by showing a hierarchy of available stored procedures. The number and type of stored procedures will vary for each customer implementation.

The following diagram shows the sequence diagram of Metadata Browse Handler.

In this post, I will use an example of creating an adapter for a Class Library / Dynamic Link Library (DLL) as a sample. We will use reflection to obtain the metadata about classes and operations within the DLL. For the browse interface, I want to return each class as a category with the public methods as operations within that category. For example – I have created a simple Class Library called MyExistingLibrary that has following public classes –

1) CalcuatorService

a. double Add(double n1, double n2)

b. double Subtract(double n1, double n2)

c. double Multiply(double n1, double n2)

d. double Divide(double n1, double n2)

2) EchoService

a. string EchoString(string input)

b. string EchoStringNTimes(string input, int count)

3) EightBallService

a. public string AskQuestion()

Once the adapter is developed, new classes/methods added in the existing library, will be directly consumed by the Adapter to always return up-to-date metadata information to the consumer.

Note: As a best practice, if you have access to the code base from which the DLL was compiled and you want to WCF-enable the classes, you don’t want to use the WCF LOB Adapter SDK to build a WCF-based adapter to it. Just decorate the classes, operations and types with ServiceContract, OperationContract and DataContract attributes.

The following diagram shows the components that can be implemented by the adapter. The adapter implements two metadata handlers – Browse and Resolver. In this post, we will only talk about how to implement the Browse Handler.

After the Adapter Code Generation Wizard is used to generate the code required for adapter development, use the following steps to provide a sample implementation of IMetadataBrowseHandler interface.

Step 1: Implement Browse method in Adapter implementation of IMetadataBrowseHandler

This interface has one key method called

public MetadataRetrievalNode[] Browse(string nodeId, int childStartIndex,int maxChildNodes, TimeSpan timeout);

where

nodeId

Each item in the hierarchy of the metadata explorer has a node ID. This item can be a category or an operation. The category can have sub-categories. Ensure a unique node ID is assigned to each item.

childStartIndex

Index of first child to return

maxChildNodes

Maximum number of children to return

MetadataRetrievalNode[]

Returns the child nodes for the supplied node ID.

This method is responsible for supporting the “Browse Metadata” functionality in the adapter. Before implementing this method, Adapter Developer needs to design the anticipated tree to return in the metadata explorer. For DLL adapter, my design is to show the available public classes in the DLL as Categories and public methods within each class as Operations of that particular category. Another alternative could have been to show all the DLLs available in a particular location, where each DLL name will be main category and the classes within it becoming the sub-categories. It is up to the Adapter Developer to provide the best possible representation of the existing application that will make sense to the adapter consumer. In SAP Adapter, for example, the Adapter Developer may categorize using SAP taxonomy such as RFC, BAPI and IDOC.

Here is the sample implementation of my Browse method for DLL adapter.

public MetadataRetrievalNode[] Browse(string nodeId, int childStartIndex, int maxChildNodes, TimeSpan timeout)

{

List<MetadataRetrievalNode> nodes = new List<MetadataRetrievalNode>();

Assembly loadedAssembly = this.Connection.LoadedAssembly;

// start building the tree

if (MetadataRetrievalNode.Root.NodeId.CompareTo(nodeId) == 0)

{

// go through the DLL and retrieve all public classes

Type[] types = loadedAssembly.GetTypes();

foreach (Type type in types)

{

if (type.IsClass )

{

MetadataRetrievalNode node = new MetadataRetrievalNode(type.Namespace + "." + type.Name);

node.Description = type.ToString();

node.DisplayName = type.Name;

node.IsOperation = false;

nodes.Add(node);

}

}

}

else

{

// go through each node and bring out operation

string categoryName = nodeId;

// catch exception here, if can't resolve the type

Type category = loadedAssembly.GetType(categoryName);

if (category != null)

{

nodes.Clear();

MethodInfo[] methods = category.GetMethods();

foreach (MethodInfo method in methods)

{

string declaringType = method.DeclaringType.Name;

// This ID is used by IMetdataResolver-ResolveOperationMetadata implementation to

// resolve input and ouptput parameters for this operation

string uniqueNodeId = category.Namespace + “.” + category.Name + “/” + method.Name;

MetadataRetrievalNode node = new MetadataRetrievalNode(uniqueNodeId);

node.DisplayName = method.Name;

node.Description = method.ToString();

node.IsOperation = true;

if( method.IsPublic && !"Object".Equals(declaringType)) nodes.Add(node);

}

}

}

return nodes.ToArray();

}

The interaction between the Adapter Consumer and Browse function is using WCF. In WCF LOB Adapter SDK, there is a Metadata Service that implements a contract IMetadataRetrievalContract containing Browse, Search and GetMetadata operations.

Step 2: Test Adapter Browse Handler functionality

1) Using WCF Channel Programming

This method shows how Adapter’s Browse functionality can be called using WCF channel programming.

class Program

{

static void Main(string[] args)

{

// Create the instance of the binding

MyExistingLibraryAdapterBinding binding = new MyExistingLibraryAdapterBinding();

EndpointAddress ea = new EndpointAddress("reflect://@c:/temp/MyExistingLibrary.dll");

ChannelFactory<IMetadataRetrievalContract> channelFactorytr = new ChannelFactory<IMetadataRetrievalContract>(binding);

channelFactory.Open();

IMetadataRetrievalContract proxy = channelFactory.CreateChannel(ea);

Console.WriteLine("\nOutputting metadata tree:");

MetadataRetrievalNode[] nodes = OutputMetadataTree(proxy, null, 0);

// Generate WSDL from the metadata retrieval nodes

Console.WriteLine("\nGenerating WSDL for " + binding.Name + "...\n");

System.Web.Services.Description.ServiceDescription sd = proxy.GetMetadata(nodes);

sd.Write("c:\\temp\\MyContract.wsdl");

Console.WriteLine("WSDL generated!");

// Close channel factory

channelFactory.Close();

}

/// <summary>

/// This method recursively browses through each node and prints its name out on the

/// console.

/// </summary>

private static MetadataRetrievalNode[] OutputMetadataTree(IMetadataRetrievalContract proxy, MetadataRetrievalNode node, int depth)

{

for (int i = 0; i < depth; i++)

{

if (i < depth - 1)

{

Console.Write(" ");

}

else

{

Console.Write("└──");

}

}

Console.WriteLine(node == null ? "Root" : node.NodeId);

MetadataRetrievalNode[] rootNodes = proxy.Browse(node == null ? "/" : node.NodeId, 0, 100);

foreach (MetadataRetrievalNode childNode in rootNodes)

{

OutputMetadataTree(proxy, childNode, depth + 1);

}

return rootNodes;

}

}

Here is the expected output, when this console program is run.

Outputting metadata tree:

Root

└── MyExistingLibrary.EchoService

└── MyExistingLibrary.EchoService/EchoString

└── MyExistingLibrary.EchoService/EchoStringNTimes

└── MyExistingLibrary.CalculatorService

└── MyExistingLibrary.CalculatorService/Add

└── MyExistingLibrary.CalculatorService/Subtract

└── MyExistingLibrary.CalculatorService/Multiply

└── MyExistingLibrary.CalculatorService/Divide

└── MyExistingLibrary.EightBallService

└── MyExistingLibrary.EightBallService/AskQuestion

Generating WSDL for MyExistingLibraryAdapterBinding...

WSDL generated!

2) Using Add Adapter Service Reference VS Plug-In

Right-click on a non-BizTalk VS Project and select “Add Adapter Service Reference…”. This will bring up the Add Adapter Service Reference UI. Once the adapter binding is registered with the WCF configuration system, the adapter binding will show up in the Select a binding drop down box. Select your adapter binding, enter Connection Uri and click on Connect. If the connection is successful, you should be able to see the browse results in left-hand tree pane labeled as Select a category.

3) Using Consume Adapter Service BizTalk Project VS Plug-In

Create a BizTalk Project in Visual Studio for experiencing Consume Adapter Service BizTalk Project VS Plug-In. Right-click on this project, select “Add Generated Items > Consume Adapter Service …”. This will bring up the Consume Adapter Service UI. This UI has same browse behavior as the Add Adapter Service Reference UI with the exception that click on OK after selecting operations generates Schemas instead of a .NET proxy.

Next Post: Resolving Metadata

Implementing Metadata Browse Handler.docx