The Connected Services Framework Component Programming Model
- Consumer Endpoint—A Web service endpoint maps a Uniform Resource Identifier (URI) to a class. The URI is provided to the consumer class at initialization time so that it can be monitored for responses. The URI may be shared between instances of the consumer classes if desired to allow for a single response endpoint.
- Consumer Class—Class types contain fields and methods - each consumer class implements the appropriate methods supported by the service it uses.
- Consumer Methods—Methods implement a computation or action—most of the Connected Services Framework consumer methods use a Message class as a parameter. A separate context parameter is passed as well to describe the common characteristics of the request.
- Message Classes—Message classes are classes that have few (if any) methods - they are structures that hold the data that will be sent to the Connected Services Framework components via WSE. There are message classes for the requests as well as message classes for their associated responses.
How are responses returned?
The 2.5 version of the consumer SDK manages the responses through the .NET event mechanism. The user of the consumer class will register for callbacks to be executed when a response returns. The user may register for callbacks based on the return action associated with the message. A single callback may be used for all actions or separate ones may be used for each action type. The response will be delivered to the callback along with a context object containing data about the message. Note that the user only needs to register for responses they are interested in, any response that is not handled by the user will be discarded.
Do Participants Have to Be Web Services?
Participant endpoints used in the Connected Services Framework can use either the HTTP or TCP protocol. Participant endpoints only have to be capable of emitting WSDL. Participant endpoints can be WSE 2.0 web services, ASP .Net (ASMX) web services, Windows Forms applications, and even Windows Services:
- An example HTTP endpoint: http://csf-test-session/Session/SessionManagerAdmin.ashx
- An example TCP endpoint: soap.tcp://csf-test-session:8002/Session/SessionManagerAdmin
Does The Session Component Support TCP?
The Session component supports participants that use either the TCP or HTTP protocol. All calls made into the Session component, however, should be made using the HTTP protocol. The CreateSessionRepsonse message contains that URIs that should be used to communicate with the Session component after Session is created.
What Are the Characteristics of Calls from the Connected Services Framework Consumer Classes to the Connected Services Framework Components?
- All calls to the Connected Services Framework components are asynchronous.
- All consumer method calls return "void".
- All consumer methods take a context object containing appropriate addressing and state information required for the request.
- The consumer objects are not linked to the Connected Services Framework components—in other words, the consumer objects make asynchronous WSE calls to the components.
- The life-cycles of the consumer objects and the component objects are not directly related. For example, creating a ServiceCatalogClient object does not cause the Connected Services Framework Service Catalog component to do anything—the Connected Services Framework Service Catalog component may create objects, or interact with UDDI, but only after the ServiceCatalogClient object sends the Service Catalog component a WSE message.
- All responses received by the consumer classes will be returned as events to the caller.
Using this approach, applications can make use of the Connected Services Framework components with no direct dependencies on WSE, and no dependencies on the Connected Services Framework component implementation. A project reference to the WSE DLL is still required.
Can I Call the Connected Services Framework Components Without Using the Connected Services Framework Consumer Classes?
While it is possible to call the Connected Services Framework components directly, use of the Connected Services Framework consumer classes to call the Connected Services Framework components is strongly encouraged. Alternate methods of calling the Connected Services Framework components may not be supported.
Overview of the Consumer SDK
The consumer SDK consists of a set of client classes designed to communicate with the four core CSF connectors. There is a single client class for each connector. Each of the client classes is designed to simplify the sending of requests to a service and to process the responses from that server. All the available actions supported on a connector are concrete methods on the matching client. Each of these methods takes a request message containing the data for the request and a message context containing state and addressing information for the request. When the response returns from the connector, it will be delivered to the user via the .NET event mechanism to a callback of their choice. All communication is asynchronous.
Prior to using the consumer classes, the user will have to perform some initialization calls to set them up correctly. There are two required calls. The first is to set the URI for the responses managed by the consumer components. This will become a WSE endpoint that the consumer classes will monitor for responses to requests. These responses will be captured, relevant header information extracted from them, and the body of the message de-serialized, then the message itself will be delivered to the appropriate callback of the user. If multiple consumer clients are in use, then may all share the same endpoint or the user may decide to have a separate end point for each consumer client.
The second operation required in initializing the consumer clients is to specify the callbacks for the responses. Each response to a client is a unique action and you register callbacks with the client based on the action; associating a callback method to that response. There is no requirement that each response return to a separate callback, all responses may be directed to a single callback if desired.
In cases where the application is already a WSE application and is already processing messages, some additional work is required if you want to use only one end point. Since the processing of responses requires that the consumer classes have the responses run through an end point that is managed by them, the application can integrate with the consumer classes by inheriting from one of the base consumer classes, the SdkBase, instead of the usual SoapServer and implementing one required method that provides support for the de-serialization of the message bodies as they arrive. This approach will then deliver all the messages received via the same callback mechanism. These messages do not have to be responses returned by CSF or related to CSF in any way. For the fullest support of the message processing, the application will have to implement a method to provide a type based on the action in the message. This type represents the type of the class that the message body can be de-serialized in to. If this type is not known or is ambiguous, a null may be returned and the messages will be delivered as an XmlElement and the application may decide how to process the raw XML.
WSDL support is provided for the consumer classes. Each of them has an appropriate set of methods indicating what responses they are expecting to process. This allows the endpoint to be queried and a WSDL returned. However this support is only provided when a single consumer class is registered on an endpoint. If multiple consumer classes are sharing endpoints, the first one to register the endpoint will return its WSDL and the other methods will not be returned. the messages will be processes if sent to that endpoint however. In cases where the service is running under IIS and using an HTTP endpoint, the WSDL will be generated based on the class configured in the web.config in the httpHandlers section.
The consumer SDK also supports the concept of the unhandled message. These are messages for which there is no registered callback to handle them. A handler may be registered for these messages and all that are not already handled will be delivered to that callback. This can provide a way to capture and log unexpected messages arriving at an application due to addressing errors or unexpected changes in other services.
Key Consumer Supporting Classes
The following classes are used with the consumer clients in sending or receiving messages.
The message context class provides the addressing data to send a message. It is filled out by the sender and used when sending messages to the connectors. Depending on the consumer class used, there will be additional information required in the context. The Session consumer relies upon a SessionId to deliver messages to participants in the session and that has to be provided in the context when sending a message.
This class is the main container of data when a message is delivered in a callback to the application. It contains the header property containing the expected header information. and the body property with the message data. Note that a MessageContext may be extracted from the message, it will be created from the data in the ClientMessage and may used in the ReplyToRequest() method available on the consumer classes. This method will take the context and reverse the appropriate addresses and send the reply.
This is the base class for the consumer client classes themselves. This class may be sub-classed within an application to build an application component that integrates in to the consumers response processing. The GetActionType() method must be implemented to return a valid type for an action in the message to support the de-serialization of the message prior to delivery to the application.
The base SDK classes are in the Microsoft.Csf.Sdk.dll. This DLL is required for all users of the consumer classes. Each Implementation of a client adds in additional DLLs for the client class and it associated message classes. The additional required DLLs for the API are:
Profile Manager: Microsoft.Csf.ProfileManager.dll, Microsoft.Csf.Sdk.ProfileManagement.dll, Microsoft.Csf.IdentityManager.dll, Microsoft.Csf.Utilities.dll
Session: Microsoft.Csf.Session.dll, Microsoft.Csf.Sdk.Session.dll, Microsoft.Csf.Utilities.dll
Identity Manager: Microsoft.Csf.IdentityManager.dll, Microsoft.Csf.Sdk.IdentityManagement.dll, Microsoft.Csf.Utilities.dll
Service Catalog: Microsoft.Csf.Sdk.ServiceCatalog.dll, Microsoft.Csf.ServiceCatalog.dll
The following example shows the initialization and sending of a message using the consumer client classes. In this case, the ServiceCatalogClient provides the example code. The task is a simple lookup of the URL for a service from the service catalog connector.
Initialization done at startup, registering the end point as well as the callback for the response. Note that the ServiceCatalogClient is constructed with the "from" address indicating the message source. This will be used later in the sending of the message if the "from" is not supplied in the context passed in on the send. The data in the context will override the defaults used in the creation of the client if they are provided. This example sends from a TCP endpoint as the test program is a simple windows app.
Uri myUri =
ServiceCatalogClient m_client = new ServiceCatalogClient(new AddressInformation(myUri));
The sending of the request.
GetUrisRequest cr = new
cr.ServiceUuids = new string;
cr.ServiceUuids = "c9fabc6b-0971-4c20-b042-bddda0abf0ef";
MessageContext context = new MessageContext();
context.To = new AddressInformation("http://localhost/ServiceCatalog/ServiceCatalogConnector.ashx");
context.Credentials = new UserNameToken("User account", "User password");
The response callback.
GetServiceUrisResponse(object sender, CsfMessageEventArgs e)
ClientMessage msg = (ClientMessage) e.Message;
GetUrisResponse getResp = (GetUrisResponse) msg.Body;
if(getResp.ResultCode != 0)
MessageBox.Show("Error: [" + getResp.ErrorMessage + "] error code [" + getResp.ResultCode +"]");
// Normal processing
StringBuilder builder = new StringBuilder();
foreach(string s in getResp.ServiceUris)
MessageBox.Show("URIs: [" + builder.ToString() + "]");