Chapter 11. Legacy Integration

<feature><title>In This Chapter</title> </feature>

Introduction

Windows Communication Foundation provides a unified messaging API that provides the capability to integrate with a number of legacy technologies. This chapter focuses on the Windows Communication Foundation’s capability to integrate with COM+ and non–Windows Communication Foundation MSMQ (Microsoft Message Queuing) applications.

COM+ Integration

Windows Communication Foundation provides a rich environment for creating distributed applications. If one has a substantial investment in component-based application logic hosted in COM+, one can use the Windows Communication Foundation to extend one’s existing logic rather than having to rewrite it. A common scenario is when one wishes to expose existing COM+ or Enterprise Services business logic through web services.

When an interface on a COM+ component is exposed as a web service, the specification and contract of that service are determined by an automatic mapping to be performed at application initialization time. The conceptual model for this mapping is as follows:

  • There is one service for each exposed COM class.

  • The contract for the service is derived directly from the selected component’s interface definition.

  • The operations in that contract are derived directly from the methods on the component’s interface definition.

  • The parameters for those operations are derived directly from the COM interoperability type corresponding to the component’s method parameters.

  • Default addresses and transport bindings for the service are provided in a service configuration file, but these can be reconfigured as required.

Note

The contracts for the generated Windows Communication Foundation services are tied to the underlying COM+ application’s interfaces and configuration.

Modifying the COM+ component methods automatically results in an updated service when the application is next started. However, a modification to the number of interfaces does not automatically update the available services. In the latter scenario, one will need to rerun the COM+ Service Model Configuration tool (ComSvcConfig.exe).

The authentication and authorization requirements of the COM+ application and its components continue to be enforced when used as a web service. If the caller initiates a web service transaction, components marked as transactional enlist within that transaction scope.

The steps that are required to expose a COM+ component’s interface as a web service without modifying the component are as listed here:

  1. Determine whether the COM+ component’s interface can be exposed as a web service.

  2. Select an appropriate hosting mode.

  3. Use the COM+ Service Model Configuration tool (ComSvcConfig.exe) to add a web service for the interface.

Supported Interfaces

Not all types of interfaces can be exposed as a web service. Here is a list of the types that cannot be exposed:

  • Interfaces that accept object references as parameters

  • Interfaces that accept types that are not compatible with the .NET Framework COM Interop conversions

  • Interfaces for applications that have application pooling enabled when hosted by COM+

  • Interfaces of components that are marked as “private” to the application

  • COM+ infrastructure interfaces

  • Interfaces from the system application

  • Interfaces from managed components that have not been added to the Global Assembly Cache (GAC)

Selecting the Hosting Mode

As is stated in the documentation, COM+ can expose web services in one of the following three hosting modes.

COM+ Hosted

The web service is hosted within the application’s dedicated COM+ server process (Dllhost.exe). This mode requires the application to be explicitly started before it can receive web service requests. The COM+ options Run as an NT Service and Leave Running When Idle can be used to prevent idle shutdown of the application and its services. This mode has the benefit that it provides both web service and DCOM access to the server application.

Web Hosted

The web service is hosted within a web server worker process. This mode does not require the COM+ application to be active when the initial request is received. If the application is not active when this request is received, it is automatically activated before the request is processed. This mode also provides both web service and DCOM access to the server application, but it incurs a process hop for web service requests. This typically requires the client to enable impersonation. In the Windows Communication Foundation, this can be done with the SetSspiSettings method and the Impersonation enumeration value.

Note

Like other Windows Communication Foundation services, the security settings for the exposed service are administered through roles and web host settings. COM+ application roles are enforced, whereas traditional DCOM security settings such as the DCOM machinewide permissions settings are not.

Web Hosted In-Process

The web service and the COM+ application logic are hosted within the web server worker process. This provides automatic activation of the web hosted mode without incurring the process hop for web service requests. The disadvantage is that the server application cannot be accessed through DCOM.

Using the COM+ Service Model Configuration Tool

The mechanism used to configure COM+ interfaces to be exposed as web services is the COM+ Service Model Configuration command-line tool (ComSvcConfig.exe). This tool will be used in this exercise to expose a COM+ business object.

The calling convention and command-line switches for ComSvcConfig are as shown here:

ComSvcConfig.exe /install | /list | /uninstall [/application:<ApplicationID | 
Using the COM+ Service Model Configuration ToolApplicationName>] [/contract<ClassID | ProgID | *,InterfaceID | InterfaceName | *>] [/
Using the COM+ Service Model Configuration Toolhosting:<complus | was>] [/webSite:<WebsiteName>] [/webDirectory:<WebDirectoryName>] [/mex]
Using the COM+ Service Model Configuration Tool [/id] [/nologo] [/verbose] [/help]

Note

One must be an administrator on the local computer to use ComSvcConfig.exe.

Table 11.1 describes the modes that can be used with ComSvcConfig.exe.

Table 11.1. Modes That Can Be Used with ComSvcConfig.exe

Option

Description

/install

Configures a COM+ interface for Service Model integration. Short form /i.

/uninstall

Removes a COM+ interface from Service Model integration. Short form /u.

/list

Queries for information about COM+ applications and components that have interfaces that are configured for Service Model integration. Short form /l.

Table 11.2 describes the option flags that can be used with ComSvcConfig.exe.

Table 11.2. Flags That Can Be Used with ComSvcConfig.exe

Option

Description

/application:< ApplicationID | ApplicationName >

Specifies the COM+ application to configure.

Short form /a.

/contract:< ClassID | ProgID | *, InterfaceID | InterfaceName | * >

Specifies the COM+ component and interface to configure as a service contract.

Short form /c.

Although the wildcard character (*) can be used when one specifies the component and interface names, doing so is not recommended because one might expose interfaces one did not intend to expose.

/allowreferences

Specifies that object reference parameters are allowed.

Short form /r.

/hosting:< complus | was >

Specifies whether to use the COM+ hosting mode or the web hosting mode.

Short form /h.

Using the COM+ hosting mode requires explicit activation of the COM+ application. Using the web hosting mode allows the COM+ application to be automatically activated as required. If the COM+ application is a library application, it runs in the Internet Information Services (IIS) process. If the COM+ application is a server application, it runs in the Dllhost.exe process.

/webSite:< WebsiteName >

Specifies the website for hosting when web hosting mode is used (see the /hosting flag).

Short form /w.

If no website is specified, the default website is used.

/webDirectory:< WebDirectoryName >

Specifies the virtual directory for hosting when web hosting is used (see the /hosting flag).

Short form /d.

/mex

Adds a Metadata Exchange (MEX) service endpoint to the default service configuration to support clients that want to retrieve a contract definition from the service.

Short form /x.

/id

Displays the application, component, and interface information as IDs.

Short form /k.

/nologo

Prevents ComSvcConfig.exe from displaying its logo.

Short form /n.

/verbose

Outputs additional tool progress information.

Short form /v.

/help

Displays the usage message.

Short form /?.

Exposing a COM+ Component as a Windows Communication Foundation Web Service

This series of exercises will focus on a scenario in which a school has an application for registering students. The business logic for this application resides within an Enterprise Service component hosted in COM+. The goal is to expose that component as a web service that can be consumed both by the school’s web-based management application and from a Windows Forms-based smart client application.

This exercise involves taking a legacy COM+ component and exposing it as a Windows Communication Foundation service that is consumed in the smart client application. To begin, examine the COM+ component to understand its functionality.

Note

Because projects in this exercise require strong names, several of the projects are signed. When opening the solution, one might be prompted for the passwords for the key files used. The password for these files is PERCY123marta.

  1. Copy the code associated with this chapter downloaded from http://www.cryptmaker.com/WindowsCommunicationFoundationUnleashed to the folder C:WCFHandsOn. The code is all in a folder called LegacyIntegration, which has three subfolders: COM, COMPlus, and MSMQ.

  2. Open the solution COMPlus.sln in the Before subdirectory of the COMPlus subfolder.

  3. Open the Students.cs file of the Student Management Application that is included in that solution. The contents are reproduced in Listing 11.1.

Because this component is being placed within COM+, it contains several attributes. The ApplicationName attribute identifies the name for the COM+ application this class will be installed into. Also note that the interface and the class are attributed with GUIDs, and the class is attributed with a ProgID. Even though this was written in .NET, the component will live in COM+ with the attributes providing the information necessary for consistent Interop registration.

This class provides methods to the user interface for additions, updates, and retrieval of basic student information. For additions and updates, the changes are sent to an MSMQ using the System.Messaging libraries. In this exercise, only the Add method will be invoked.

Example 11.1. The COM+ Code

using System;
using System.Collections.Generic;
using System.Text;
using System.Messaging;
using System.Data;
using System.Data.SqlClient;
using System.EnterpriseServices;
using System.Runtime.InteropServices;


[assembly: ApplicationName("StudentManagement")]
[assembly: ApplicationID("2C9BFEA5-005D-4218-8C69-A03F6B9037BA")]
[assembly: ApplicationActivation(ActivationOption.Server)]
[assembly: ApplicationAccessControl(false, AccessChecksLevel = AccessChecksLevelOption
The COM+ Code.Application)]

namespace WCFHandsOn
{
    [Guid("1C5677EC-8046-4c5b-B361-BA354CFA3DB3")]
    public interface IStudents
    {
        string Add(string FirstName, string LastName, string PhoneNumber);
        bool Update(string ID, string FirstName, string LastName, string PhoneNumber);
        bool Delete(string ID);
        System.Collections.ArrayList GetAll();

    }

    [Guid("E4A5D9FD-3B5F-4598-9E42-EC8D1329EE9D")]
    [ProgId("StudentManagement.Students")]
    public class Students : ServicedComponent, IStudents
    {
        public Students() {}

        string qPath = @"FormatName:DIRECT=OS:w2k3eeprivate$school";
        public string Add(
            string FirstName,
            string LastName,
            string PhoneNumber)
        {
            //For any modifications to the data, we place the
            //request on a queue.

            //First we generate a System.Messaging.Message for the queue.
            try
            {
                string ID = Guid.NewGuid().ToString();
                Student student = new Student(ID, FirstName, LastName, PhoneNumber);
                System.Messaging.Message msg = GenerateAddMessage(student);

                //Now we place it to the queue
                PlaceMessageOnQueue(msg);

                //This is a new student, return the GUID
                return ID;
            }
            catch (Exception e)
            {
                //Debug.WriteLine(e.ToString());
                throw e;
            }
        }

In the next few steps, the IStudents interface is to be exposed from the COM+ application as a web service using the Windows Communication Foundation. That will enable it to be used as a WS-*–compatible service from clients on any platform. To expose this as a Windows Communication Foundation service, the ComSvcConfig utility will be used. To use that utility, one will need to create a virtual directory to house the service:

  1. Choose Start, Programs, Administrative Tools, Internet Information Services Manager.

  2. Create a directory at C:WCFHandsOnLegacyIntegrationComPlus BeforeStudentManagementService.

  3. Navigate to the default website and create a new Virtual Directory. The alias for the directory should be WCFHandsOn_StudentMgmt, and the path for the directory is C:WCFHandsOnLegacyIntegrationComplusBeforeStudentManagementService.

Now the ComSvcConfig utility can be used:

  1. Open the SDK Command Prompt by choosing All Programs, Microsoft Windows SDK, CMD Shell.

  2. Navigate to C:WCFHandsOnLegacyIntegrationComPlusBefore.

  3. Run the following script to register the COM+ component. It configures the IStudents interface on the component from the StudentManagement application, hosts it using IIS (as opposed to COM+), and places the files in the virtual directory WCFHandsOn_StudentMgmt:

    C:WindowsMicrosoft.NETFrameworkv3.0Windows Communication Foundation ComSvcConfig.exe
    The COM+ Code /i /application:StudentManagement /contract:StudentManagement.Students,Students* /hosting
    The COM+ Code:was /webDirectory:WCFHandsOn_StudentMgmt /mex
    
  4. Navigate to the virtual directory created earlier and confirm that a service—named after the interface—was generated and placed there.

  5. Examine the web.config file that was generated. Its contents should be similar to the configuration shown in Listing 11.2. It includes a section called comContracts, which includes the GUID for the contract, the interface name, and lists the exposed methods. The name of the service references GUIDs as well:

    Example 11.2. The Generated Configuration <?xml version=“1.0” encoding=“utf-8”?>

    <configuration>
        <system.serviceModel>
            <behaviors>
                <serviceBehaviors>
                    <behavior name="ComServiceMexBehavior">
                        <serviceMetadata httpGetEnabled="true" />
                        <serviceDebug includeExceptionDetailInFaults="false" />
                    </behavior>
                </serviceBehaviors>
            </behaviors>
            <bindings>
                <wsHttpBinding>
                    <binding name="comNonTransactionalBinding">
                        <reliableSession enabled="true" />
                    </binding>
                    <binding
                        name="comTransactionalBinding"
                        transactionFlow="true">
                        <reliableSession enabled="true" />
                    </binding>
                </wsHttpBinding>
            </bindings>
            <comContracts>
                <comContract contract="{1C5677EC-8046-4C5B-B361-BA354CFA3DB3}"
                    name="IStudents"
                    namespace="http://tempuri.org/1C5677EC-8046-4C5B-B361- BA354CFA3DB3"
                    requiresSession="true">
                    <exposedMethods>
                        <add exposedMethod="Add" />
                        <add exposedMethod="Update" />
                        <add exposedMethod="Delete" />
                        <add exposedMethod="GetAll" />
                    </exposedMethods>
                </comContract>
            </comContracts>
            <services>
                <service
                    behaviorConfiguration="ComServiceMexBehavior"
                    name="{2C9BFEA5-005D-4218-8C69-A03F6B9037BA},
    The Generated Configuration <?xml version=“1.0” encoding=“utf-8”?> {E4A5D9FD-3B5F-4598-9E42-EC8D1329EE9D}">
                    <endpoint
                        address="IStudents"
                        binding="wsHttpBinding"
                        bindingConfiguration="comNonTransactionalBinding"
                        contract="{1C5677EC-8046-4C5B-B361-BA354CFA3DB3}" />
                    <endpoint
                        address="mex"
                        binding="mexHttpBinding" bindingConfiguration=""
                        contract="IMetadataExchange" />
                </service>
            </services>
        </system.serviceModel>
    </configuration>
    

To test the service, open the service file in your web browser. Navigate to http://localhost/WCFHandsOn_StudentMgmt/Service.svc. If the service is functioning properly, a page is displayed that identifies the location of the WSDL and how to consume the service, as in Figure 11.1.

Web page generated from the metadata of the new Windows Communication Foundation service.

Figure 11.1. Web page generated from the metadata of the new Windows Communication Foundation service.

Record the address of the WSDL specified in the first box on this screen because it is used in the next part of the exercise.

Referencing in the Client

The next step in the exercise is to connect the service to a Windows Forms client. While a Windows Forms client is used here, the service can be consumed by any application that can interact with a web service. Proceed as follows:

  1. Open the SDK Command Prompt by choosing All Programs, Microsoft Windows SDK, CMD Shell.

  2. Run the following script to generate proxy code and a configuration file that can be used:

    "C:Program FilesMicrosoft SDKsWindowsv6.0Binsvcutil.exe " http://localhost
    Referencing in the Client/WCFHandsOn_StudentMgmt/StudentManagement.Students.svc /out:proxy.cs
    

    This generates two files: proxy.cs and output.config.

  3. Add both files to the Teacher Application project.

  4. Rename the configuration file to Web.Config for the website and App.Config for the Windows Forms application.

    The application provides an interface that gives the capability to add new students to the system.

  5. Open the Proxy.cs file and note the name of the new proxy class that was created. This class will be referenced in the code for executing the service.

  6. Modify the code for the button to add the students using the new web service. This is done by adding the following code to the btnUpdate_Click method:

    StudentsClient proxy = new StudentsClient("Students");
    string result = proxy.Add(tbFirstName.Text,tbLastName.Text,tbPhone.Text);
    MessageBox.Show("Student Added!");
    
  7. Enter a first name, last name, and phone number into the application, and click Add to test it. The results should be as shown in Figure 11.2. A message box will be displayed identifying that the student has been added.

    The user interface for the sample.

    Figure 11.2. The user interface for the sample.

The preceding steps confirm successful integration with a legacy COM+ application, and the provision of a platform-agnostic web service. They also confirm the successful use of the SvcUtil tool to generate proxies for that new service, proxies that have been incorporated into a Windows Forms client.

Calling a Windows Communication Foundation Service from COM

In addition to exposing COM+ applications as Windows Communication Foundation services, there’s also a need to expose Windows Communication Foundation services to legacy COM applications. Whether it’s a legacy application such as Lotus Notes, a custom application written in Visual Basic 6 or VBScript, there are a number of COM centric scenarios where the ability to call Windows Communication Foundation services from COM clients could prove useful.

In this next exercise, a Windows Communication Foundation service will be exposed via COM and consumed by a VBScript component.

Building the Service

Follow these steps to create the Windows Communication Foundation service:

  1. Create a new Console Application project named service in C:WCFHandsOnLegacyIntegrationCOMBefore.

  2. Add a reference to System.Configuration.

  3. Add a reference to System.ServiceModel.

  4. Add a referemce to System.Runtime.Serialization.

  5. Add a new class and name it ICreditCheck.cs.

  6. Populate ICreditCheck.cs with the following code to create the service interface:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.ServiceModel;
    
    namespace WCFHandsOn
    {
            [ServiceContract]
        public interface ICreditCheck
        {
            [OperationContract]
            int CheckCredit(
                  string socialSecurityNumber,
                  string firstName,
                  string lastName);
        }
    }
    
  7. Add a new class and name it service.cs.

  8. Populate service.cs with the following code to create the service interface:

    using System;
    using System.ServiceModel;
    using System.Runtime.Serialization;
    
    namespace WCFHandsOn
    {
        public class CreditCheck : WCFHandsOn.ICreditCheck
        {
            public int CheckCredit(
                    string socialSecurityNumber,
                    string firstName,
                    string lastName)
            {
                //650 is the average US Credit Score
                return 650;
    
            }
        }
    
    }
    
  9. Rename program.cs to servicehost.cs.

  10. Populate servicehost.cs with the following code to create the service interface:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Configuration;
    using System.ServiceModel;
    
    namespace WCFHandsOn
    {
          class CustomServiceHost
          {
            static void Main(string[] args)
            {
    
               Uri baseAddress = new Uri(
                  ConfigurationManager.AppSettings["baseAddress"]);
               // Instantiate new ServiceHost
    
               ServiceHost service = new ServiceHost(
                  typeof(CreditCheck), baseAddress);
    
    
               service.Open();
    
               Console.WriteLine("Credit Check Service is online.");
    
               Console.ReadLine();
            }
        }
    }
    
  11. Add a new Application Configuration file.

  12. Populate the configuration file with the configuration shown in Listing 11.3:

    Example 11.3. Service Configuration

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <appSettings>
        <!-- use appSetting to configure base address provided by host -->
        <add key="baseAddress" value="http://localhost:8080/WCFHandsOn" />
      </appSettings>
      <system.serviceModel>
        <services>
          <service
              name="WCFHandsOn.CreditCheck"
              behaviorConfiguration="CreditCheckServiceBehavior">
            <endpoint address="CreditCheck"
                      binding="wsHttpBinding"
                      bindingNamespace="http://WCFHandsOn.Samples.ChapterSix"
                      contract="WCFHandsOn.ICreditCheck" />
    
            <endpoint address="mex"
                      binding="mexHttpBinding"
                      contract="IMetadataExchange" />
          </service>
        </services>
    
    
        <behaviors>
          <serviceBehaviors>
            <behavior name="CreditCheckServiceBehavior">
              <serviceMetadata httpGetEnabled="True"/>
              <serviceDebug includeExceptionDetailInFaults="False" />
            </behavior>
          </serviceBehaviors>
        </behaviors>
    
      </system.serviceModel>
    
    </configuration>.
    

Building the Client

These next few steps are for constructing the client:

  1. Start the service created in the first part of the exercise.

  2. Open the SDK Command Prompt by choosing All Programs, Microsoft Windows SDK, CMD Shell.

  3. Create a proxy for the service using SvcUtil.exe:

    C:Program FilesMicrosoft SDKsWindowsv6.0Binsvcutil.exe " http://localhost/WCFHandsOn
    Building the Client/mex_/out:CreditCheckClient.cs
    

    This will generate two files: CreditCheckClient.cs and output.config. For a COM client, only the CreditCheckClient.cs file will be used.

  4. Create a new class library project named Client.

  5. Delete Class1.cs from the project.

  6. Add the CreditCheckClient.cs file to the project.

  7. In the property folder for the Client project, open the AssemblyInfo.cs file, reproduced in Listing 11.4, and assign the guid EE540164-B991-4600-83D2-1A66EC5DE20A and set ComVisible = true.

    Example 11.4. Assembly Information Fileusing System;

    using System.Reflection;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices;
    
    // General Information about an assembly is controlled through the following
    // set of attributes. Change these attribute values to modify the information
    // associated with an assembly.
    [assembly: AssemblyTitle("client")]
    [assembly: AssemblyDescription("")]
    [assembly: AssemblyConfiguration("")]
    [assembly: AssemblyCompany("")]
    [assembly: AssemblyProduct("client")]
    [assembly: AssemblyCopyright("Copyright ©  2005")]
    [assembly: AssemblyTrademark("")]
    [assembly: AssemblyCulture("")]
    
    // Setting ComVisible to false makes the types in this assembly not visible
    // to COM componenets.  If one needs to access a type in this assembly from
    // COM, set the ComVisible attribute to true on that type.
    [assembly: ComVisible(true)]
    [assembly: CLSCompliant(true)]
    // The following GUID is for the ID of the typelib
    // if this project is exposed to COM
    [assembly: Guid("EE540164-B991-4600-83D2-1A66EC5DE20A")]
    
    // Version information for an assembly consists of the following four values:
    //
    //      Major Version
    //      Minor Version
    //      Build Number
    //      Revision
    //
    // One can specify all the values or
    // one can default the Revision and Build Numbers
    // by using the '*' as shown below:
    [assembly: AssemblyVersion("1.0.0.0")]
    [assembly: AssemblyFileVersion("1.0.0.0")]
    
  8. Right-click on the Client project and select Properties.

  9. Click on the Signing tab.

  10. Under Choose a Strong Name Key File, select New from the drop-down list.

  11. Name the key file CreditCheckClient.

  12. Build the client.

  13. Open the SDK Command Prompt by choosing All Programs, Microsoft Windows SDK, CMD Shell.

  14. Navigate to the bin directory for the Client project.

  15. Run the regasm tool against the client library:

    regasm /tlb:CreditCheckClient client.dll
    
  16. Install the assembly into the GAC. Type the following at the SDK Command Prompt:

    gacutil /i client.dll
    

Building the VBScript File

The next task is to build a VBScript file that will use the client to call the service. In the last exercise, the client.dll assembly was placed in the GAC and registered. In the next few steps, a VBScript file will be created that will use COM to call the CreditCheck service.

  1. Run Notepad.exe.

  2. Enter the following script into the new Notepad document:

    Set proxy = GetObject("service:address=http://localhost:8080/WCFHandsOn/CreditCheck,
    Building the VBScript File binding=wsHttpBinding, contract={D144C6BB-0C7B-3671-846C- AE1EEBFD3FA4}")
    
    WScript.Echo "Credit Score: " & cstr(proxy.CheckCredit("123456789", "John", "Smith"))
    
    Set proxy = nothing
    

    The service is called from script code using GetObject. This is a latebound call created by passing in the service address, the binding, and the contract. The contract is identified by a GUID.

    The GUID is not the same GUID that was specified in the assembly. This GUID is the one that is attached to the ICreditCheck interface in the Windows Registry. This can be found with the regedit tool by searching for the interface name (in this case, ICreditCheck).

    After creating the proxy, one can call CheckCredit() passing the expected information: Social Security number, first name, and last name.

  3. Save the file as CreditCheckClient.vbs.

Now the solution is ready to be tested.

Testing the Solution

Follow these steps to test the solution:

  1. Start the service.

  2. Run the CreditCheckClient.vbs file.

A message box that displays Credit Score: 650 should appear.

Using COM, the CreditCheckClient.vbs file makes a call into the proxy client assembly, which calls through to the service. This serves to demonstrate a means by which one can empower legacy applications and languages to participate as clients not only to classic web services, but also to services using transports, such as TCP.

Integrating with MSMQ

The next exercise will be to create a Windows Communication client for a System.Messaging service. The client application will use the Windows Communication Foundation to send messages to an MSMQ queue. The service uses the facilities of System.Messaging to detect and retrieve the incoming messages. In scenarios like this one, in which the Windows Communication Foundation is used to exchange messages via MSMQ with non-Windows Communication Foundation applications, the Windows Communication Foundation’s MsmqIntegrationBinding is used to transmit the messages.

The client Windows Communication Foundation application has a dummy contract to mimic invoking a service operation, but, in reality, the message is consumed by an MSMQ service. The service contract is IStudent, which defines a one-way service that is suitable for use with queues. An MSMQ message does not have an Action header. It is not possible to map different MSMQ messages to operation contracts automatically. Therefore, there can be only one operation contract. If more than one operation contract is to be defined for the service, the application must provide information as to which header in the MSMQ message (for example, the Label or correlationID) can be used to decide which operation contract to dispatch.

The MSMQ message also does not contain information about which headers are mapped to the different parameters of the operation contract. So, there can be only one parameter in the operation contract. The parameter is of type generic MsmqMessage (MsmqMessage<T>) and contains the underlying MSMQ message. The type T in the generic MsmqMessage (MsmqMessage<T>) class represents the data that is serialized into the MSMQ message body. In this exercise, a Student type is serialized into the MSMQ message body.

Creating a Windows Communication Foundation Service That Integrates with MSMQ

In this scenario, a Windows Communication Foundation service will be created that provides the capability to update student records. The first step will be to create a struct that will contain the student information used when adding or updating a record. That step will be followed by the creation of the service and client.

The result will be a Windows Communication client that posts a message to MSMQ that is later received by a non–Windows Communication Foundation application monitoring the queue. That receiving application will process the message in the appropriate way, modifying the student records as needed.

Creating the Request

For the purposes of this exercise, a student will be represented by four pieces of information: studentID, firstName, lastName, and phoneNumber. All these pieces of information will need to be conveyed in the body of a message when a student is added or updated. These first few steps are for defining a struct with the four student information items:

  1. Create a new class library project named StudentManagementRequest in C:AppsWCFHandsOnExerciseThreeBefore.

  2. Delete Class1.cs.

  3. Using Solution Explorer, add a new class named StudentManagementRequest.cs.

  4. Populate the new class with the following code to create the struct:

    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace WCFHandsOn
    {
        [Serializable]
        public struct StudentManagementRequest
        {
            public string studentID;
            public string firstName;
            public string lastName;
            public string phoneNumber;
        }
    }
    

Creating the Service

The next steps are for building the non-Windows Communication Foundation student information service. It is to receive messages requesting updates to student records via System.Messaging:

  1. Create a new class library project named Service in C:WCFHandsOnLegacyIntegrationMSMQBefore.

  2. Delete Class1.cs.

  3. Add a reference to System.Messaging.

  4. Add a reference to System.Configuration.

  5. Add a reference to the StudentManagementRequest project created earlier.

  6. Using Solution Explorer, add a new Application Configuration file to identify the queue by which the application will receive messages, a private queue on the local machine named school:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
        <appSettings>
            <!-- use appSetting to configure MSMQ queue name -->
            <add key="queueName" value=".private$school" />
    
    </appSettings>
    </configuration>
    
  7. Using Solution Explorer, add a new class to the project named Program.cs.

    Open Program.cs, and add the following namespaces to the top of the file:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Messaging;
    using System.Configuration;
    using System.Data;
    using System.Data.SqlClient;
    namespace WCFHandsOn
    
  8. Now proceed to add the code that forms the core of the service. The code reads the queueName specified in the App.config file and confirms that a queue with that name exists. If the queue does not exist, a transactional queue with the specified name is created. After designating a method called ProcessMessage as the one to be called when a message arrives, the code calls the BeginReceive() method of the System.Messaging MessageQueue object to start monitoring the queue for incoming messages.

    static void Main(string[] args)
    {
      // Create a transaction queue using System.Messaging API
      // You could also choose to not do this and instead create the
      // queue using MSMQ MMC--make sure you create a transactional queue
      if (!MessageQueue.Exists(ConfigurationManager.AppSettings["queueName"]))
        MessageQueue.Create(ConfigurationManager.AppSettings["queueName"], true);
      //Connect to the queue
      MessageQueue Queue = new MessageQueue(ConfigurationManager.AppSettings["queueName"]);
      Queue.ReceiveCompleted += new ReceiveCompletedEventHandler(ProcessMessage);
      Queue.BeginReceive();
      Console.WriteLine("Message Processing Service is running");
      Console.ReadLine();
    }
    
  9. Next, add the code for the aforementioned ProcessMessage() method, shown in Listing 11.5. The method reads the body of each received message into a StudentManagementRequest object and inspects the message label. The Windows Communication Foundation client, which will be programmed next, uses the message label to indicate what action the service is to take in processing the message:

    Example 11.5. The ProcessMessage() Method

    public static void ProcessMessage(Object source,
       ReceiveCompletedEventArgs asyncResult)
    {
         try
            {
               // Connect to the queue.
               MessageQueue Queue = (MessageQueue)source;
    
               // End the asynchronous receive operation.
               System.Messaging.Message msg = Queue.EndReceive(asyncResult.AsyncResult);
    
                 msg.Formatter = new System.Messaging.XmlMessageFormatter(new Type[] {typeof
    The ProcessMessage() Method(StudentManagementRequest) });
                 StudentManagementRequest request = (StudentManagementRequest)msg.Body;
                 switch (msg.Label)
                 {
                        case "Add":
                            AddStudent(request);
                            break;
                        case "Update":
                            UpdateStudent(request);
                            break;
                        case "Delete":
                            DeleteStudent(request.studentID);
                            break;
                        default:
                            Console.WriteLine("The label of the message is of an unknown type
    The ProcessMessage() Method or not set correctly.");
                            break;
    
                 }
    
                 Queue.BeginReceive();
               }
               catch (System.Exception ex)
               {
                   Console.WriteLine(ex.Message);
               }
    }
    
  10. Add the code for the methods, UpdateStudent(), AddStudent() and DeleteStudent() that the ProcessMessage() method uses in dealing with incoming requests:

    private static void UpdateStudent(StudentManagementRequest s)
    {
         Console.WriteLine("Just updated student {0} {1} with a phone number of {2}", s
    The ProcessMessage() Method.firstName, s.lastName, s.phoneNumber);
    }
    
    private static void AddStudent(StudentManagementRequest s)
    {
         Console.WriteLine("Just added student {0} {1} with a phone number of {2}", s
    The ProcessMessage() Method.firstName, s.lastName, s.phoneNumber);
    }
    
    private static void DeleteStudent(string studentID)
    {
         Console.WriteLine("Just deleted student with ID {0}", studentID);
    }
    

Creating the Client

These next few steps are for creating the client application that will use the student information service to maintain student records:

  1. Open the Client application found at C:WCFHandsOnLegacyIntegrationMSMQBefore.

  2. Add a reference to System.ServiceModel.

  3. Using Solution Explorer, add a reference to the StudentManagementRequest project that contains the struct by which student records are defined.

  4. Using Solution Explorer, add a new class called StudentManagementProxy.cs. That class will contain the service interface and proxy code for communicating with the student information service.

  5. Add the code shown in Listing 11.6 to the StudentManagementProxy class, taking note of several key elements. The service interface provides only a single operation: ProcessMessage. This might seem odd because there are Add, Update, and Delete operations on the service. When using the Windows Communication Foundation to integrate with non-Windows Communication Foundation applications via MSMQ, it is not possible to map different MSMQ messages to operation contracts automatically. Instead, one defines a single operation that takes an MSMQ message of a particular type as a parameter. In this case, the parameter is the StudentManagmentRequest defined earlier. How the message is to be processed by the application that receives the message is identified by the label on the message.

    Example 11.6. Code for the Client of the Student Information Service

    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.MsmqIntegration;
    
    namespace WCFHandsOn
    {
    
        [System.ServiceModel.ServiceContractAttribute(
          Namespace = "http://www.tempuri.org")]
        public interface IStudentManagement
        {
    
            [System.ServiceModel.OperationContractAttribute(
               IsOneWay = true, Action = "*")]
            void ProcessMessage(MsmqMessage<StudentManagementRequest> msg);
        }
    
        public interface IStudentManagementChannel:
          IStudentManagement,
          System.ServiceModel.IClientChannel
        {
        }
    
        public partial class StudentManagementProxy:
          System.ServiceModel.ClientBase<IStudentManagement>,
          IStudentManagement
        {
    
            public StudentManagementProxy()
            {
            }
    
            public StudentManagementProxy(string configurationName)
                :
                    base(configurationName)
            {
            }
    
            public StudentManagementProxy(
              System.ServiceModel.Channels.Binding binding)
                :
                    base(binding.Name.ToString())
            {
            }
    
            public StudentManagementProxy(
              System.ServiceModel.EndpointAddress address, System.ServiceModel.Channels
    Code for the Client of the Student Information Service.Binding binding)
                :
                    base(address.Uri.ToString(), binding.Name.ToString())
            {
            }
    
            public void ProcessMessage(MsmqMessage<StudentManagementRequest> msg)
            {
                base.Channel.ProcessMessage(msg);
            }
        }
    }
    
  6. Using Solution Explorer, add a new Application Configuration file.

    Populate the configuration file as shown in the following code:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
    
    
          <system.serviceModel>
    
                <client>
                      <!-- Define NetProfileMsmqEndpoint -->
                      <endpoint      name="StudentManagementEndpoint"
                                        address="msmq.formatname:DIRECT=OS: .private$school"
                                        binding="msmqIntegrationBinding"
                            bindingConfiguration="Binding2"
                            contract="WCFHandsOn.IStudentManagement">
                      </endpoint>
                </client>
    
                <bindings>
                      <msmqIntegrationBinding>
                            <binding name="Binding2">
                        <security mode="None" />
                    </binding>
                      </msmqIntegrationBinding>
                </bindings>
    
          </system.serviceModel>
    
    </configuration>
    
  7. Modify the Program.cs file to include the code in Listing 11.7. The code generates a message from the information entered into the user interface and sends it to the queue. Messages that arrive on the queue are processed by the student information service:

    Example 11.7. Client User Interface Code

    static void Main(string[] args)
          {
          Console.WriteLine("Student Management Client is Now Online");
          Console.WriteLine("---------------------------------------");
          AddStudent("Marc", "Mercuri", "123456789");
    
                Console.WriteLine();
                Console.WriteLine("Press <ENTER> to terminate client.");
                Console.ReadLine();
          }
    
      private static void AddStudent(
        string firstName,
        string lastName,
        string phoneNumber)
      {
          using (StudentManagementProxy p = new
             StudentManagementProxy(
               "StudentManagementEndpoint"))
          {
    
             using (TransactionScope tx = new
               TransactionScope(
                 TransactionScopeOption.Required))
             {
                 // Submit a job to add two numbers
                 StudentManagementRequest request =
                   new StudentManagementRequest();
                 MsmqMessage<StudentManagementRequest> msg =
                   new MsmqMessage<StudentManagementRequest>(
                     request);
    
                 request.firstName = firstName;
                 request.lastName = lastName;
                 request.phoneNumber = phoneNumber;
                 request.studentID = System.Guid.NewGuid().ToString();
                 msg.Label = "Add";
                 msg.Body = request;
                 Console.WriteLine(
                   "Client attempting to add student "
                    + firstName
                    + " "
                    + lastName
                    + " with phone number :"
                    + phoneNumber);
                 p.ProcessMessage(msg);
    
                 //Commit the transaction
                 tx.Complete();
    
            }
    
    
        }
    
    
    
    }
    

Testing

With the code completed, it is time to test the application.

  1. Using Solution Explorer, right-click on the Client project and select Debug, New Instance.

  2. Using Solution Explorer, right-click on the Service project and select Debug, New Instance.

The client and server consoles will be displayed. The information entered in the client’s Main method will send a message to the queue using the Windows Communication Foundation, which will be received by the waiting service using MSMQ.

To verify that the message was transferred, check the window of the Service application. The information provided should be displayed as in Figure 11.3.

Service output indicating that a student has been added.

Figure 11.3. Service output indicating that a student has been added.

Summary

Very few enterprises start from scratch; they’ve evolved over time and have multiple legacy systems in place. Leveraging the information and business processes in these legacy applications is of keen interest to the enterprise. This chapter demonstrated how well the Windows Communication Foundation works with two of the most popular types of legacy applications, those that utilize COM+ and MSMQ.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset