4. Step 4: Implementing the Metadata Handlers
The metadata support is probably the most distinguishing feature of the adapters written using the WCF LOB Adapter SDK. In this step, you will implement the interfaces that turn
that magic into realty. Table 11 lists the interfaces.
Table 11. Metadata Interfaces
Interface | Description |
---|
IMetadataBrowseHandler | This
interface represents the browse capability of the WCF LOB Adapters. You
must implement this interface regardless of your adapter functionality. |
IMetadataSearchHandler | Optional. This interface represents the search capability of the WCF LOB Adapters. |
IMetadataResolverHandler | This
interface is used when the WCF LOB Adapter SDK needs to generate the
proxy files in case of .NET applications or XSD specifications in case
of BizTalk applications. |
4.1. Implementing the IMetadataBrowseHandler Interface
To enable the browse capability of the adapter, you have to implement the IMetadataBrowse Handler interface from the Microsoft.ServiceModel.Channels.Common namespace. The WCF LOB Adapter Development Wizard generated the derived HotelAdapterMetadataBrowseHandler skeleton class for you.
The IMetadataBrowseHandler interface is located in the Microsoft.ServiceModel.Channels. dll assembly and is defined as follows:
public interface IMetadataBrowseHandler : IConnectionHandler, IDisposable
{
MetadataRetrievalNode[] Browse(string nodeId,
int childStartIndex,
int maxChildNodes,
TimeSpan timeout);
}
We've put a description of the parameters accepted by the Browse method in Table 12.
Table 12. IMetadataBrowseHandler.Browse Method Parameter
Parameter | Description |
---|
nodeId | The node identifier. Browse method evaluates the nodeId and returns either a collection of child nodes, if any, or a collection of operations associated with the given nodeId. |
childStartIndex | The index of the first child to return. |
maxChildNodes | The maximum number of child nodes to return. |
Timeout | The maximum time to wait to complete the operation. |
The Browse method returns an array of the MetadataRetrievalNode objects representing either the operation or category type. Table 13 lists the public properties of the MetadataRetrievalNode class.
Table 13. MetadataRetrievalNode Public Properties
Property | Description |
---|
Description | Gets or sets the node description. |
Direction | Gets or sets the direction (inbound or outbound) in which the node is being retrieved. |
DisplayName | Gets or sets the node name to be displayed by UI. |
IsOperation | Gets or sets the value indicating that the node is operation. |
NodeId | Gets or sets the NodeId, which uniquely identifies the node. The NodeId is usually a concatenation of the node path and node name. The node name is not necessarily the same as the DisplayName. |
Root | Read-only. Gets the root node. |
When you finish
implementation and deploy the project, you will be able to explore the
operations supported by the adapter using the Add Adapter Service
Reference or Consume Adapter Server reference tools, as shown in Figure 12.
Now let's get to the code and implement the IMetadataBrowseHandler interface. Here is the process to follow:
In Visual Studio, open the HotelAdapterMetadataBrowserHandler.cs file.
Find the IMetadataBrowseHandler region, and replace it with the following code:
/// <summary>
/// Retrieves an array of MetadataRetrievalNodes from the target
/// system. The browse will return nodes starting from the
/// childStartIndex in the path provided in absoluteName, and
/// the number of nodes returned is limited by maxChildNodes.
/// The method should complete within the specified timespan or
/// throw a timeout exception.
/// If absoluteName is null or an empty string, return nodes starting
/// from the root + childStartIndex.
/// If childStartIndex is zero, then return starting at the node
/// indicated by absoluteName (or the root node if
///absoluteName is null or empty).
/// </summary>
public MetadataRetrievalNode[] Browse(string nodeId
, int childStartIndex
, int maxChildNodes, TimeSpan timeout)
{
//WCF LOB SDK UI tools by deafult start with the root "\" node.
//for the root node there is only one category "Hotel Operations"
if (MetadataRetrievalNode.Root.NodeId.CompareTo(nodeId) == 0)
{
// Create an inbound and outbound category
//under the root node
MetadataRetrievalNode node = new
MetadataRetrievalNode("HotelOperations");
node.NodeId = "HotelOperations";
node.DisplayName = "Hotel Operations";
node.Description = @"This category contains inbound and
outbound operations supported by the HotelAdapter.";
node.Direction = MetadataRetrievalNodeDirections.Inbound |
MetadataRetrievalNodeDirections.Outbound;
node.IsOperation = false;
return new MetadataRetrievalNode[] { node };
}
// if user selected "HotelOperations" in
//the "Select Category" control populated
//return two operations
//OnGuestArrived in Inbound scenario
//GetRooms in outbound
else if ("HotelOperations".CompareTo(nodeId) == 0)
{
// Create outbound operation
MetadataRetrievalNode nodeOutbound =
new MetadataRetrievalNode("Hotel/GetRooms");
nodeOutbound.NodeId = "Hotel/GetRooms";
nodeOutbound.DisplayName = "GetRooms";
nodeOutbound.Description =
"This operation returns the number of available rooms.";
nodeOutbound.Direction =
MetadataRetrievalNodeDirections.Outbound;
nodeOutbound.IsOperation = true;
// Create inbound operation
MetadataRetrievalNode nodeInbound =
new MetadataRetrievalNode("Hotel/OnGuestArrived");
nodeInbound.NodeId = "Hotel/OnGuestArrived";
nodeInbound.DisplayName = "OnGuestArrived";
nodeInbound.Description =
"This operation notifies of client arrival.";
nodeInbound.Direction = MetadataRetrievalNodeDirections.Inbound;
nodeInbound.IsOperation = true;
return new MetadataRetrievalNode[]
{ nodeOutbound, nodeInbound };
}
return null;
}
#endregion IMetadataBrowseHandler Members
In Visual Studio, open the HotelAdapterHandlerBase.cs file.
Find the Dispose method, and comment out the NotImplementedException exception.
Save and build the project.
As you can see, the code is
not complicated, but it indeed requires accuracy. Although the sample
adapter deals with only two operations, adapters for the real-life LOB
systems will likely have to provide dozens if not hundreds or even
thousands of operations. Imagine if you as an adapter developer had to
add an if-else block every time you
wanted to expose a new operation. If this is the case, then the very
idea of "automatic metadata discovery" turns into "manual metadata
discovery" with very little advantages over classic WCF services if at
all.
To decide whether your LOB
system is a good candidate for being front-ended by the WCF LOB
Adapters, you have to take into account how the metadata is organized
and whether new operations will be accessible by the previously written
code, ideally with no changes (the "write-code-once" concept). If
getting to new metadata requires serious modifications of the existing
code, this should probably be taken as an alarm bell, because it
indicates that the system is not optimally designed. Please note that
the "write-code-once" concept applies not only to the browse handler but
also to the search and resolve handlers that we will discuss in the
next two sections. All three metadata interfaces constitute the metadata
capabilities of the WCF LOB Adapters and have to be written according
to the concept. To see the "write-code-once" concept in action, we
recommend you take a close look at the Contoso Adapter supplied with the
WCF LOB Adapter SDK. This adapter is a good example of how metadata in
the form of SQL Server stored procedures can be accessed in a unified
manner.
4.2. Implementing the IMetadataSearchHandler interface
To enable the optional "search" capability of the adapter, you have to implement the IMetadataSearchHandler interface from the Microsoft.ServiceModel.Channels.Common namespace. The WCF LOB Adapter Development Wizard generated the derived Hotel AdapterMetadataSearchHandler skeleton class for you.
The IMetadataSearchHandler interface is located in the Microsoft.ServiceModel.Channels. dll and is defined as follows:
public interface IMetadataSearchHandler : IConnectionHandler, IDisposable
{
MetadataRetrievalNode[] Search(string nodeId, string searchCriteria,
int maxChildNodes, TimeSpan timeout);
}
The Search method returns an array of the MetadataRetrievelNode
objects that satisfy specified search criteria. We introduced this
class earlier in the "Implementing IMetadataBrowseHandler" section.
We've listed the parameters accepted by the Search method in Table 14.
Table 14. IMetadataSearchHandler.Search Method Parameters
Parameter | Description |
---|
nodeId | The node identifier to start the search from. If nodeId is null or an empty string, then the adapter will start evaluating nodes from the root node (/). |
searchCriteria | Search criteria. If not specified, the adapter should return all nodes. |
maxChildNodes | Maximum number of the child elements to return. To return all matching nodes, use the Int.Max32 value. |
Timeout | Maximum time to wait to complete the operation. |
Here is the process to implement the IMetadataSearchHandler interface:
In Visual Studio, open the HotelAdapterMetadataSearchHandler.cs file.
Find the IMetadataSearchHandler Members region, and replace it with the following code:
#region IMetadataSearchHandler Members
/// <summary>
/// Retrieves an array of MetadataRetrievalNodes
/// (see Microsoft.ServiceModel.Channels) from the target system.
/// The search will begin at the path provided in absoluteName,
/// which points to a location in the tree of metadata nodes.
/// The contents of the array are filtered by SearchCriteria and the
/// number of nodes returned is limited by maxChildNodes.
/// The method should complete within the specified timespan or
/// throw a timeout exception. If absoluteName is null or an
/// empty string, return nodes starting from the root.
/// If SearchCriteria is null or an empty string, return all nodes.
/// </summary>
public MetadataRetrievalNode[] Search(string nodeId
, string searchCriteria
, int maxChildNodes, TimeSpan timeout)
{
List<MetadataRetrievalNode> searchResult =
new List<MetadataRetrievalNode>();
searchCriteria = searchCriteria.ToLower();
//we have only two operations
// check them one by one
if ("OnClientArrived".ToLower().Contains(searchCriteria))
{
MetadataRetrievalNode nodeInbound =
new MetadataRetrievalNode("Hotel/OnClientArrived");
nodeInbound.DisplayName = "OnClientArrived";
nodeInbound.Description = @"This operation notifies
external clients of client arrival.";
nodeInbound.Direction =
MetadataRetrievalNodeDirections.Inbound;
nodeInbound.IsOperation = true;
searchResult.Add(nodeInbound);
}
if ("GetRooms".ToLower().Contains(searchCriteria))
{
MetadataRetrievalNode nodeOutbound =
new MetadataRetrievalNode("Hotel/GetRooms");
nodeOutbound.DisplayName = "GetRooms";
nodeOutbound.Description = @"This operation returns the
number of available rooms.";
nodeOutbound.Direction =
MetadataRetrievalNodeDirections.Outbound;
nodeOutbound.IsOperation = true;
searchResult.Add(nodeOutbound);
}
return searchResult.ToArray();
}
#endregion IMetadataSearchHandler Members
Save and build the project.
4.3. Implementing the IMetadataResolverHandler Interface
To provide ASDK with information on the operations and data types supported by the HotelAdapter, you have to implement the IMetadataResolverHandler interface from the Microsoft.ServiceModel.Channels.Common namespace. As we mentioned earlier, the IMetadataResolverHandler interface is used by ASDK when it generates proxy files or message type specifications.
The WCF LOB Adapter Development Wizard generated the derived HotelAdapterMetadata ResolverHandler skeleton class for you.
The IMetadataResolverHandler interface is located in the Microsoft.ServiceModel. Channels.dll and is defined as follows:
public interface IMetadataResolverHandler :
IConnectionHandler, IDisposable
{
bool IsOperationMetadataValid(string operationId,
DateTime lastUpdatedTimestamp, TimeSpan timeout);
bool IsTypeMetadataValid(string typeId,
DateTime lastUpdatedTimestamp, TimeSpan timeout);
OperationMetadata ResolveOperationMetadata(string operationId,
TimeSpan timeout,
out TypeMetadataCollection extraTypeMetadataResolved);
TypeMetadata ResolveTypeMetadata(string typeId, TimeSpan timeout,
out TypeMetadataCollection extraTypeMetadataResolved);
}
Table 15 describes what each method does.
Table 15. IMetadataResolverHandler Methods
Method | Description |
---|
IsOperationMetadataValid | Returns a Boolean value indicating whether operation metadata is valid. For the HotelAdapter, we assume that operation metadata is always valid. |
IsTypeMetadataValid | Returns a Boolean value indicating whether type metadata is valid. For the HotelAdapter, we assume that type metadata is always valid. |
ResolveOperationMetadata | Maps supplied operationId to corresponding Microsoft.ServiceModel.Channels.Common.OperationMetadata. |
ResolveTypeMetadata | Maps supplied typeId to corresponding Microsoft.ServiceModel.Channels.Common.TypeMetadata. |
Here is the process to implement the IMetadataResolverHandler interface:
In Visual Studio, open the HotelAdapterMetadataResolverHandler.cs file.
Find the IsOperationMetadataValid method, and modify it to return true.
Find the IsTypeMetadataValid method, and modify it to return true.
Find the ResolveOperationMetaData method, and replace it with the following code:
/// <summary>
/// Returns an OperationMetadata object resolved from absolute
/// name of the operation metadata object.
/// The method should complete within the specified time
/// span or throw a timeout exception.
/// </summary>
public OperationMetadata ResolveOperationMetadata(string operationId,
TimeSpan timeout,
out TypeMetadataCollection extraTypeMetadataResolved)
{
extraTypeMetadataResolved = null;
ParameterizedOperationMetadata om =
new ParameterizedOperationMetadata(operationId, operationId);
// set this if you want this operation to belong
// to this interface name
// in the generated proxy, the interface name
// will be HotelService and the client implementation name
// will be HotelServiceClient.
om.OperationGroup = "HotelService";
// set the operation namespace to be same as service namespace
om.OperationNamespace = HotelAdapter.SERVICENAMESPACE;
switch (operationId)
{
case "Hotel/GetRooms":
om.DisplayName = "GetRooms";
om.OriginalName = "originalGetRooms";
OperationParameter parm1 =
new OperationParameter("hotelName",
OperationParameterDirection.In,
QualifiedType.StringType, false);
parm1.Description = @"This string will
contain hotel name for which
number of rooms is being requested";
OperationResult result =
new OperationResult(new
SimpleQualifiedType(XmlTypeCode.String), false);
om.Parameters.Add(parm1);
om.OperationResult = result;
return om;
case "Hotel/OnClientArrived":
om.DisplayName = "OnClientArrived";
om.OriginalName = "originalOnClientArrived";
OperationParameter pName =
new OperationParameter("FirstName",
OperationParameterDirection.In,
QualifiedType.StringType, false);
pName.Description = "Client's first name.";
OperationParameter pLastName=
new OperationParameter("LastName",
OperationParameterDirection.In,
QualifiedType.StringType, false);
pLastName.Description = "Client's last name.";
om.Parameters.Add(pName);
om.Parameters.Add(pLastName);
om.OperationResult = null;
return om;
return null;
}