Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

WS-Enumeration for WCF/WF

4.86/5 (5 votes)
4 Feb 2011CPOL5 min read 26.2K   220  
In this article, I describe the design and implementation of a WS-Enumeration for WCF.

Introduction

This article could be considered as a continuation of the WS-Transfer Service for Workflow article written by Roman Kiss. I've implemented the WS-Enumeration spec based on the approach suggested by Roman Kiss. Utilizing the sequence workflows for service or protocols development attracts me since it is a very natural way to do it. In many cases, you need to just look at the scheme in order to understand how the service or protocol operates.

The purpose of WS-Enumeration is to enumerate the elements in different structures (it could be a list, tree etc.). So one may suppose that LINQ could be helpful for it. Thinking of it, I recall the LINQ to Tree - A Generic Technique for Querying Tree-like Structures article written by Colin Eberhardt. Colin's main idea was to apply the "LINQ to XML" technique to non-XML tree structures. Colin developed the ILinqToTree<T> adapter interface and a set of extension methods which allows to execute queries in a unified way for different tree structures whether it is a file system or a database or something else. Since it is right what I need for WS-Enumeration, I utilized Colin's approach here.

Background

In contrast to WS-Transfer, WS-Enumeration is a stateful protocol developed to enumerate elements based on filter. Once the enumeration is done, the client pulls the instances page-by-page.

The protocols works as follows:

ws_enum_diagram.png

  1. Client sends the enumerate request.
  2. Based on the enumeration request parameters, the service constructs the enumeration, the context (identifier of enumeration), and returns it to the client.
  3. Client sends a pull request, which contains the "enumeration context" and the number of instances to be returned. Because the enumeration context is required, a pull request is only valid after a successful enumerate request and before the enumeration context expires. This ordering is why we call WS-Enumeration stateful.
  4. The server returns data to the client in the pull response. In order to represent instances in XML, we utilize the IXmlSerializable interface.
  5. The client keeps pulling data until it receives an end of sequence notice.

Enumerate

When processing the enumerate request, the service should prepare the enumeration based on the supplied filter. An enumerate request may have Filter and Dialect attributes. The Filter expression could be XPath, regex, or something else. This Filter behavior should be reflected in the Dialect attribute. In case the service receives an enumerate request with an unsupported dialect, a fault should be send to the client. Once the enumeration and the context are prepared, the service returns the "context" to the client.

Pull

The Pull request should contain the obligatory "context" parameter. The context is like a unique identifier of the enumeration stored in the service. The pull operation requests the next N object instances for a given enumeration context, where N is provided in the request. The Service returns at most N XML representations of these object instances in the response. Each of the returned objects satisfies the filter in the enumerate request.

GetStatus

The "enumeration context" has a limited life time. The GetStatus request is designed for getting the expiration time of the specified context.

Renew

This request asks the service to prolong the context life time. This operation could be helpful during long-time pull operations.

Release

The Release request initiates the deletion of the context.

Concept and design implementation

concept.png

Virtually, the project could be divided in to two loosely coupled parts: communication layer and business logic.

  • Communication layer - WCF-Service.
  • Business logic - (Adapter -> Workflow -> LinqToTree) the layer which processes the client's requests.

The adapter is a connector between the communication layer and the business logic. The service behavior extensions allow to configure the service behavior through a configuration file. The value of the "type" attribute determines the type of the adapter we want to utilize.

XML
<configuration>
    <system.serviceModel>
        <diagnostics wmiProviderEnabled="true"/>
        <services>
            <service name="WSEnumeration.WSEnumerationService" 
                  behaviorConfiguration="WxfServiceExtension">
                <endpoint address="net.tcp://localhost:11111/PublicStorage"
                    binding="netTcpBinding"
                    bindingConfiguration="Binding1"
                    contract="WSEnumeration.IWSEnumeration"/>
            </service>
        </services>

        <bindings>
            <netTcpBinding>
                <binding name="Binding1" transactionFlow="true"/>
            </netTcpBinding>
        </bindings>

        <extensions>
            <behaviorExtensions>
                <add name="WSEnumerationAdapter" 
                  type="WSEnumeration.ServiceAdapterBehaviorElement, 
                        WSEnumeration, Version=1.0.0.0, Culture=neutral, 
                        PublicKeyToken=null"/>
                <add name="logger" 
                  type="RKiss.Logger.LoggerBehaviorElement, Logger, 
                        Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
            </behaviorExtensions>
        </extensions>
    
        <behaviors>
            <serviceBehaviors>
            <behavior name="WxfServiceExtension">
                <WSEnumerationAdapter StoragePath="c:\"
                    ExpirationType="T|D|H|M"
                    Filtering="true"
                    FilterDialect="XPath"
                    type="WSEnumeration.Adapters.WFAdapter, WSEnumeration"
                    WorkflowTypeEnumerate=
                       "WSEnumerationWorkflow.FileSystem.WorkflowEnumerate, 
                        WSEnumerationWorkflow"
                    WorkflowTypePull=
                       "WSEnumerationWorkflow.FileSystem.WorkflowPull, 
                        WSEnumerationWorkflow"
                    WorkflowTypeRenew=
                       "WSEnumerationWorkflow.FileSystem.WorkflowRenew, 
                        WSEnumerationWorkflow"
                    WorkflowTypeGetStatus=
                       "WSEnumerationWorkflow.FileSystem.WorkflowGetStatus, 
                        WSEnumerationWorkflow"
                    WorkflowTypeRelease=
                       "WSEnumerationWorkflow.FileSystem.WorkflowRelease, 
                        WSEnumerationWorkflow"/>
                <logger enable="true" logAfterReceiveRequest="true" 
                        logBeforeSendReply="true"/>
            </behavior>
            </serviceBehaviors>
        </behaviors>
    </system.serviceModel>
</configuration>

Here are the meanings of the WSEnumerationAdapter element attributes:

AttributeDescription
StoragePathPath to the resource storage. It could be a file system path or a connection string in the case of a database or something else.
ExpirationTypeThe expiration time may be either an absolute time or a duration. This attribute determines which expiration type the service supports. "T|D|H|M" means: T-absolute time, D - duration in days, H - duration in hours, M duration in minutes.
FilteringValue of this attribute reflects the filtering support.
FilterDialectValue of this attribute reflects the dialects which the service is able to understand. For example, if the value is "XPath|Regex", it means that the service can understand XPath and Regex filter expressions.
TypeAdapter type
WorkflowTypeEnumerateType of the workflow class for the Enumerate request.
WorkflowTypePullType of the workflow class for the Pull request.
WorkflowTypeRenewType of the workflow class for the Renew request.
WorkflowTypeGetStatusType of the workflow class for the GetStatus request.
WorkflowTypeReleaseType of the workflow class for the Release request.

So through a configuration file, we are potentially able to configure our service to work with a file system or a database or something else. Of course, just in cases where we've already developed the corresponding business logic layer. In this project, I've developed a business logic intended to work with a file system.

Test

In order to demonstrate how the WS-enumeration works, I have created a demo application which allows the user to navigate the file system on a service host.

In order to set the Service host resource directory, you have to set the "StoragePath" attribute in the configuration file. Now you are ready to start the service. The service host conversation with the client is displayed by the logger written by Roman Kiss. The client application represents a single form with a DataGridView which displays the content of some directory located at the service host.

test_app.png

Image 4

Conclusion

Like language patterns (Singleton, etc.) or architectural patterns (MVC, MVVM etc.), WS-* spec provides us a way to standardize the development of service oriented applications (SOA).

At this moment, the project is in developing stage, and does not have production quality. Nevertheless, it can be used for research or test purposes.

History

  • 04/02/2011 - Initial release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)