Step 6: Implement the Metadata Resolve Handler for the Echo Adapter
Time to complete: 45 minutes
In this step, you implement the Microsoft.ServiceModel.Channels.Common.IMetadataResolverHandler
interface to resolve operation and type metadata for the echo adapter. Regardless of your adapter's capability, you must implement this interface. The Adapter Development Wizard automatically generates the derived class called EchoAdapterMetadataResolverHandler for you.
In the following section, you update the EchoAdapterMetadataResolverHandler class to get a better understanding on how to implement this interface. When you complete this step, you have a working metadata resolve handler for the echo adapter.
Prerequisites
Before you begin this step, you must have successfully completed Step 5: Implement the Metadata Search Handler for the Echo Adapter. You must also understand the following operation and type classes:
Microsoft.ServiceModel.Channels.Common.ParameterizedOperationMetadata
Microsoft.ServiceModel.Channels.Common.OperationMetadata
Microsoft.ServiceModel.Channels.Common.OperationParameter
Microsoft.ServiceModel.Channels.Common.OperationResult
Microsoft.ServiceModel.Channels.Common.TypeMetadata
Microsoft.ServiceModel.Channels.Common.StructuredTypeMetadata
Microsoft.ServiceModel.Channels.Common.TypeMember
Microsoft.ServiceModel.Channels.Common.SimpleQualifiedType
Microsoft.ServiceModel.Channels.Common.ComplexQualifiedType
The IMetadataResolverHandler Interface
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);
}
The following table describes what each method does:
Method Name | Description |
---|---|
IsOperationMetadataValid | Returns a true if the type metadata has not changed since the date and time specified |
IsTypeMetadataValid | Returns a Boolean value that indicates whether the specified type metadata is valid. |
ResolveOperationMetadata | Resolves an operation ID to corresponding Microsoft.ServiceModel.Channels.Common.OperationMetadata |
ResolveTypeMetadata | Resolves a supplied metadata typeId to a corresponding Microsoft.ServiceModel.Channels.Common.TypeMetadata . |
To implement the IsOperationMetadataValid method
In Solution Explorer, double-click the EchoAdapterMetadataResolverHandler.cs file.
In the Visual Studio editor, right-click anywhere within the editor, in the context menu, point to Outlining, and then click Stop Outlining.
In the Visual Studio editor, find the IsOperationMetadataValid method, inside this method, replace the existing with the following single statement to indicate that every specified operation metadata is valid.
return true;
To implement the IsTypeMetadataValid method
In the Visual Studio editor, find the IsTypeMetadataValid method, inside this method, replace the existing with the following single statement to indicate that every specified type metadata is valid.
return true;
To implement the ResolveOperationMetadata method
In the Visual Studio editor, find the ResolveOperationMetadata method, inside this method, replace the existing with the following to resolve the OnReceiveEcho operation, void OnReceiveEcho(Uri path, long fileLength).
extraTypeMetadataResolved = null; switch( operationId ) { case "Echo/OnReceiveEcho": ParameterizedOperationMetadata om = new ParameterizedOperationMetadata(operationId, "OnReceiveEcho"); om.OriginalName = "lobNotification"; om.Description = "This operation echoes the location and length of a file dropped in the specified file system."; om.OperationGroup = "EchoInboundContract"; om.OperationNamespace = EchoAdapter.SERVICENAMESPACE; // syntax: void OnReceiveEcho(Uri path, long fileLength) OperationParameter parmPath = new OperationParameter("path", OperationParameterDirection.In, QualifiedType.UriType, false); parmPath.Description = "Absolute path of the file"; OperationParameter parmLength = new OperationParameter("length", OperationParameterDirection.In, QualifiedType.LongType, false); parmLength.Description = "Length of the file received in this location."; om.Parameters.Add(parmPath); om.Parameters.Add(parmLength); om.OperationResult = OperationResult.Empty; return om;
Continue adding the following to resolve the Echo/EchoStrings operation, string[] EchoStrings(string data).
case "Echo/EchoStrings": om = new ParameterizedOperationMetadata(operationId, "EchoStrings"); om.OriginalName = "lobEchoStrings"; om.Description = "This operation echoes the incoming string COUNT number of times in a string array."; om.OperationGroup = "EchoOutboundContract"; om.OperationNamespace = EchoAdapter.SERVICENAMESPACE; // syntax: string[] EchoStrings(string data) OperationParameter parmData = new OperationParameter("data", OperationParameterDirection.In, QualifiedType.StringType, false); parmData.Description = "Input string"; om.Parameters.Add(parmData); om.OperationResult = new OperationResult(QualifiedType.StringType, true); return om;
Continue adding the following logic to resolve the Echo/EchoStrings operation, string[] EchoStrings(string data).
case "Echo/EchoGreetings": om = new ParameterizedOperationMetadata(operationId, "EchoGreetings"); om.OriginalName = "lobEchoGreetings"; om.Description = "This operation echoes the incoming Greeting object COUNT number of times in an array of type Greeting."; om.OperationGroup = "EchoOutboundContract"; om.OperationNamespace = EchoAdapter.SERVICENAMESPACE; // syntax: Greeting[] EchoGreetings(Greeting greeting) ComplexQualifiedType cqtGreeting = new ComplexQualifiedType("Types/GreetingType"); OperationParameter parmGreeting = new OperationParameter("greeting", OperationParameterDirection.In, cqtGreeting, false); parmGreeting.Description = "Input greeting"; om.Parameters.Add(parmGreeting); om.OperationResult = new OperationResult(cqtGreeting, true); return om;
Continue adding the following logic to resolve the CustomGreeting EchoCustomGreetingFromFile(Uri greetingInstancePath) operation.
case "Echo/EchoCustomGreetingFromFile": om = new ParameterizedOperationMetadata(operationId, "EchoCustomGreetingFromFile"); om.OriginalName = "lobEchoGreetingUsePredefinedMetadata"; om.Description = "This operation echoes the incoming Greeting object COUNT number of times in an array of type Greeting. The Greeting type metadata is created using predefined XSD file."; om.OperationGroup = "EchoOutboundContract"; om.OperationNamespace = EchoAdapter.SERVICENAMESPACE; OperationParameter parmGreetingInstancePath = new OperationParameter("greetingInstancePath", OperationParameterDirection.In, QualifiedType.UriType, false); om.Parameters.Add(parmGreetingInstancePath); ComplexQualifiedType cqtGreetingXsd = new ComplexQualifiedType("Types/CustomGreetingFromXsdType"); om.OperationResult = new OperationResult(cqtGreetingXsd, false); // resolve extra typemetadata here extraTypeMetadataResolved = new TypeMetadataCollection(); // use a predefined schema to generate metadata for this type CustomGreetingTypeMetadata tmGreetingXsd = new CustomGreetingTypeMetadata("Types/CustomGreetingFromXsdType", "CustomGreeting"); extraTypeMetadataResolved.Add(tmGreetingXsd); return om;
Continue adding the following to handle default case.
default: throw new AdapterException("Cannot resolve metadata for operation identifier " + operationId); }
To implement the ResolveTypeMetadata method
In the Visual Studio editor, find the ResolveTypeMetadata method, inside this method, replace the existing with the following to return a
Microsoft.ServiceModel.Channels.Common.TypeMetadata
object.extraTypeMetadataResolved = null; string typeNamespaceForGreeting = EchoAdapter.SERVICENAMESPACE + "/Types"; switch (typeId) { case "Types/GreetingType": StructuredTypeMetadata tmGreeting = new StructuredTypeMetadata(typeId, "Greeting"); tmGreeting.TypeNamespace = typeNamespaceForGreeting; tmGreeting.Members.Add(new TypeMember("id", QualifiedType.GuidType, false)); tmGreeting.Members.Add(new TypeMember("sentDateTime", QualifiedType.DateTimeType, false)); ComplexQualifiedType cqtName = new ComplexQualifiedType("Types/NameType"); tmGreeting.Members.Add(new TypeMember("name", cqtName, false)); tmGreeting.Members.Add(new TypeMember("greetingText", QualifiedType.StringType, false)); return tmGreeting; case "Types/NameType": StructuredTypeMetadata tmName = new StructuredTypeMetadata(typeId, "Name"); tmName.TypeNamespace = typeNamespaceForGreeting; ComplexQualifiedType cqtSalutation = new ComplexQualifiedType("Types/SalutationType"); tmName.Members.Add(new TypeMember("salutation", cqtSalutation, false)); tmName.Members.Add(new TypeMember("firstName", QualifiedType.StringType, false)); tmName.Members.Add(new TypeMember("middleName", QualifiedType.StringType, false)); tmName.Members.Add(new TypeMember("lastName", QualifiedType.StringType, false)); return tmName; case "Types/SalutationType": EnumTypeMetadata tmSalutation = new EnumTypeMetadata(typeId, "Salutation", new string[] { "Mr.", "Mrs.", "Dr.", "Ms.", "Miss" }); tmSalutation.TypeNamespace = typeNamespaceForGreeting; return tmSalutation; default: throw new AdapterException("Cannot resolve metadata for type identifier " + typeId); }
To define the custom greeting type metadata class
In Solution Explorer, right-click the Echo Adapter project, point to Add, and then click New Item.
In the Add New Item dialog box, under Templates, click Class.
In the Name text box, type CustomGreetingTypeMetadata.
Click Add.
In the Visual Studio editor, replace the existing code with the following:
using System; using System.Collections.Generic; using System.Text; using Microsoft.ServiceModel.Channels.Common; using System.Xml; using System.Xml.Schema; using System.IO; namespace Microsoft.Adapters.Samples.EchoV2 { public class CustomGreetingTypeMetadata : TypeMetadata { private const string CONST_METADATA_FILE_NAME = "Microsoft.Adapters.Samples.EchoV2.CustomGreeting.xsd"; public CustomGreetingTypeMetadata(string typeId, string typeName) : base(typeId, typeName) { this.TypeNamespace = EchoAdapter.SERVICENAMESPACE + "/PreDefinedTypes"; this.Description = " "; this.CanUseCommonCache = true; // if the nillable is not set to true, the generated proxy wraps the operation // with request and response objects this.IsNillable = true; } /// <summary> /// Override the base ExportXmlSchema to provide own /// custom XML Schema /// </summary> /// <param name="schemaExportContext"></param> /// <param name="metadataLookup"></param> /// <param name="timeout"></param> public override void ExportXmlSchema(XmlSchemaExportContext schemaExportContext, MetadataLookup metadataLookup, TimeSpan timeout) { if (schemaExportContext == null) { throw new AdapterException("Schema export context is null."); } // Read in XML Schema file or create XmlSchema object yourself Stream predefinedXsdFile = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(CONST_METADATA_FILE_NAME); XmlReader reader = XmlReader.Create(predefinedXsdFile); XmlSchema schema = XmlSchema.Read(reader, null); if (!IsComplexTypeAlreadyDefined(schemaExportContext.SchemaSet, schema)) { schemaExportContext.SchemaSet.Add(schema); if (!schemaExportContext.NamespacePrefixSet.ContainsKey(this.TypeNamespace)) { schemaExportContext.NamespacePrefixSet.Add(this.TypeNamespace, getUniqueNamespacePrefix(schemaExportContext, 0)); } } reader.Close(); } /// <summary> /// A default value cannot be set for this type metadata. /// </summary> public override bool CanSetDefaultValue { get { return false; } } /// <summary> /// Helper function to see if the schema is already defined in the /// XmlSchemaSet. /// </summary> /// <param name="oldschemaset"></param> /// <param name="newschema"></param> /// <returns></returns> public static bool IsComplexTypeAlreadyDefined(XmlSchemaSet oldschemaset, XmlSchema newschema) { // ensure correct namespace was defined in the passed-in schema foreach (XmlSchema schema in oldschemaset.Schemas(newschema.TargetNamespace)) { foreach (XmlSchemaObject newschemaObject in newschema.Items) { if (newschemaObject is XmlSchemaComplexType) { //check for the definition of complex type in the schemaset foreach (XmlSchemaObject schemaObject in schema.Items) { XmlSchemaComplexType complexType = schemaObject as XmlSchemaComplexType; // Definition of this Complex Type already exists if (complexType != null && String.Compare(complexType.Name, ((XmlSchemaComplexType)newschemaObject).Name, false, System.Globalization.CultureInfo.InvariantCulture) == 0) return true; } } } } return false; } /// <summary> /// Helper function to generate a unique namespace prefix /// </summary> /// <param name="schemaExportContext"></param> /// <param name="startSuffix"></param> /// <returns></returns> private string getUniqueNamespacePrefix(XmlSchemaExportContext schemaExportContext, int startSuffix) { string defaultPrefix = "ns"; string val = defaultPrefix + startSuffix; if (schemaExportContext.NamespacePrefixSet.ContainsValue(val)) { return getUniqueNamespacePrefix(schemaExportContext, ++startSuffix); } else { return val; } } } }
In Visual Studio, from the File menu, click Save All.
To create the custom greeting XML schema definition
In Solution Explorer, right-click the Echo Adapter project, point to Add, and then click New Item.
In the Add New Item dialog box, under Templates, click XML Schema.
In the Name text box, type CustomGreeting.
Click Add.
In Solution Explorer, right-click the CustomGreeting.xsd file and choose View Code.
In the Visual Studio editor, begin by replacing the existing code with the following code that begins the definition of the CustomGreeting schema:
<?xml version="1.0" encoding="utf-8" ?> <xs:schema id="XMLSchema1" targetNamespace="http://tempuri.org/XMLSchema1.xsd" elementFormDefault="qualified" xmlns="http://tempuri.org/XMLSchema1.xsd" xmlns:mstns="http://tempuri.org/XMLSchema1.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema"> </xs:schema>
with the following code that begins the definition of the CustomGreeting schema:
<?xml version="1.0" encoding="utf-16"?> <xsd:schema xmlns:b="http://schemas.microsoft.com/BizTalk/2003" xmlns="echov2://microsoft.adapters.samples.echov2/PreDefinedTypes" elementFormDefault="qualified" targetNamespace="echov2://microsoft.adapters.samples.echov2/PreDefinedTypes" xmlns:xsd ="http://www.w3.org/2001/XMLSchema">
Add the following to define the CustomGreeting element:
<xsd:element name="greeting" type="CustomGreeting" />
Now add the definition of the CustomGreeting complex type:
<xsd:complexType name="CustomGreeting"> <xsd:sequence> <xsd:element name="address" type="UsAddress" /> <xsd:element name="greetingText" type="xsd:string" /> </xsd:sequence> </xsd:complexType>
Continue the CustomGreeting schema definition by adding the UsAddress complex type:
<xsd:complexType name="UsAddress"> <xsd:sequence> <xsd:element minOccurs="1" maxOccurs="1" name="street1" nillable="true" type="xsd:string" /> <xsd:element minOccurs="0" maxOccurs="1" name="street2" type="xsd:string" /> <xsd:element minOccurs="1" maxOccurs="1" name="city" type="xsd:string" /> <xsd:element minOccurs="1" maxOccurs="1" name="state" type="xsd:string" /> <xsd:element name="zip" type="PostalCode" /> </xsd:sequence> </xsd:complexType>
Complete the definition of the CustomGreeting schema by adding the PostalCode simple type and the closing tag for the schema:
<xsd:simpleType name="PostalCode"> <xsd:restriction base="xsd:positiveInteger"> <xsd:pattern value="\d{5}" /> </xsd:restriction> </xsd:simpleType> </xsd:schema>
Now update the build action for this file so it is treated as an embedded resource. To do this, in the Visual Studio solution pane, right-click the file and choose Properties. Change Build Action from None to Embedded Resource.
In Visual Studio, from the File menu, click Save All.
Note
You saved your work. You can safely close Visual Studio at this time or go to the next step, Step 7: Implement the Synchronous Outbound Handler for the Echo Adapter.
What Did I Just Do?
You just implemented the metadata resolving capability for the echo adapter.
Next Steps
In the next step, you implement the synchronous outbound handler for the Echo Adapter. You then implement the synchronous inbound handler and then build and deploy the Echo Adapter.
See Also
Step 5: Implement the Metadata Search Handler for the Echo Adapter
Step 7: Implement the Synchronous Outbound Handler for the Echo Adapter
Tutorial 1: Develop the Echo Adapter