Exposing a COM Component as a WCF Web Service


Exposing a COM+ Component as a WCF Web Service

For this series of exercises, we 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+. It is desired 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, let's examine the COM+ component to understand its functionality.

Open C:\WCFHandsOn\ExerciseFive\Before\ComPlus.sln.

Within the Student Management Application, open the Students.cs file.

Because this component is being placed within COM+, it contains several attributes. The ApplicationName attribute identifies the name of the COM+ application into which this class will be installed. 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 be living in COM+, with the attributes providing the information necessary for consistent interop registration (See Listing 6.1).

Listing 6.1. The COM+ Code

[View full width]

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 .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:w2k3ee\private$\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;         }     }

As you go through the file, you'll see that 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, we will be invoking only the Add method.

In this exercise, we would like to expose the IStudents interface from the COM+ application as a Web service using WCF. This provides the capability to take this legacy component and allow it to provide a WS-* compatible service that can be consumed on any platform.

Because the component is a legacy application, it is already in COM+. To expose this as a WCF service, the ComSvcConfig utility will be used.

Before using this utility, you will need to create a virtual directory in which to house the service:

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

  2. Create a directory at C:\WCFHandsOn\ChapterFive\Before\StudentManagementService.

  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:\WCFHandsOn\ChapterFive\Before\StudentManagementService.

You are now ready to use the ComSvcConfig utility:

  1. Open a Visual Studio command prompt.

  2. Navigate to C:\WCFHandsOn\ChapterFive\Before\.

  3. Run the following script to register the COM+ Component:

[View full width]

C:\Windows\Microsoft.NET\Framework\v2.0.50727\ComSvcConfig.exe /i/application :StudentManagement/contract:StudentManagement.Students,Students* /hosting:was/webDirectory :WCFHandsOn_StudentMgmt /mex


Reviewing this script, you can see that you are configuring the IStudents interface on the component from the StudentManagement application, hosting using IIS (versus COM+) and will be placing the files in the virtual directory WCFHandsOn_StudentMgmt.

If you now navigate to the virtual directory created earlier, you'll find that a servicenamed after the interfacewas generated and placed in this directory.

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 (see Figure 6.1).

Figure 6.1. Web page generated from the metadata of your new 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. It should be noted that although in this exercise you will be using a Windows Form client to consume the service, it is for the sake of simplicity. This service can be consumed in any application that can consume services:

  1. Open a Visual Studio command prompt.

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

    [View full width]

    "C:\Program Files\Microsoft SDKs\Windows\v1.0\Bin\svcutil.exe " http://localhost /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 provides 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 your code to execute 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:

    StudentsProxy proxy = new StudentsProxy("Students"); string result = proxy.Add(tbFirstName.Text,tbLastName.Text,tbPhone.Text); MessageBox.Show("Student Added!"); proxy.Close();

  7. Enter a first name, last name, and phone number into the application, and click Add to test it (see Figure 6.2). A message box will be displayed identifying that the student has been added.

    Figure 6.2. The user interface for the sample.

You've successfully integrated with a Legacy COM+ application, and established a platform-agnostic, WS-*compliant service. You've also used the SvcUtil utility to generate proxies from this new service, and connected to a Windows Forms client.

Integrating with MSMQ

In this next exercise, we will be creating an MSMQ service. The service will send requests to a queue. A separate queue monitor application monitors this queue and retrieves the messages placed there by the service.

This scenario is not a "pure" Windows Communication Foundation scenario, meaning that one of the systems is using WCF and the other is using System.Messaging. In scenarios in which WCF will be used with native MSMQ applications, the MsmqIntegrationBinding is used.

The client application is a WCF application that uses the MsmqIntegrationBinding binding to send a durable, transactional message to the queue. The client 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 IStudents, 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 you want to define more than one operation contract 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 in itself as to 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>) represents the data that is serialized into the MSMQ message body. In this exercise, a Student type is serialized into the MSMQ message body.

Note

If you want to map different headers of the MSMQ message (for example, Label or correlationID) to different parameters, the MessagePropertyAttribute attribute has to be used.


Creating a WCF Service That Integrates with MSMQ

In our scenario, we will be creating a Windows Communication Foundation service that provides the capability to update student records. We will begin by creating a struct that will contain the student information used when adding or updating a record. This will be followed by the creation of our service and client.

The resulting solution will yield in a WCF client that posts a message to MSMQ that is later received by a non-WCF application monitoring the queue. That receiver application would take the message and process it as appropriate.

Creating the Request

During this exercise, you will be sending requests to manipulate student information. A student is represented by four pieces of information, studentID, firstName, lastName, and phoneNumber. If we add or update a student, we will need to pass all of this information in the body of the message. You will create a new project that contains a struct containing this information, and this project will later be referenced by your service and client:

  1. Create a new class library project named StudentManagementRequest in C:\Apps\WCFHandsOn\ExerciseThree\Before.

  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

Now you will create the service for the scenario:

  1. Create a new class library project named Service in C:\WCFHandsOn\ChapterFive\Before.

  2. Delete Class1.cs.

  3. Add a reference to System.Messaging.

  4. Add a reference to System.Configuration.

  5. Add a reference (project reference) to your project StudentManagementRequest.

  6. Using Solution Explorer, add a new Application Configuration file.

    The configuration for our MSMQ Integration service is listed in the following code. In addition, the file also contains the name of our MSMQ queue. For this exercise, we're using a private queue on the local machine named school.

    Modify your App.Config file to resemble the following:

    <?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 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. Next you will create the core of the service. Because this service is queue based, the code reads the queueName specified in the App.Config file and checks for its existence. If the queue does not exist, the queue is created. Note that the second parameter sent to MessageQueue.Create is TRue. This will create the queue as a transactional queue.

    The queue will be receiving messages at various times. To be notified of the arrival of a message, the code also assigns an event handler. The ProcessMessage method of this class will be called when new messages are received.

    The BeginReceive function is then called, and as its name implies, the MessageQueue object is monitoring for received messages.

    In Program.cs, enter the following code:

    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();  }

    The ProcessMessage method is called when the queue begins receiving a message. The message body is read and assigned to a StudentManagementRequest object and the message label inspected.

  9. The server determines which action to take from the message label. Based on the text of the label, corresponding methods on the object are called.

    Enter the following code:

    [View full width]

    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 (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 or not set correctly."); break; } Queue.BeginReceive(); } catch (System.Exception ex) { Console.WriteLine(ex.Message); } }

  10. The ProcessMessage function interprets the message and calls one of three methods: UpdateStudent, AddStudent, or DeleteStudent. Enter the following code to process these requests:

    [View full width]

    private static void UpdateStudent(StudentManagementRequest s) { Console.WriteLine("Just updated student {0} {1} with a phone number of {2}", s. 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. firstName, s.lastName, s.phoneNumber); } private static void DeleteStudent(string studentID) { Console.WriteLine("Just deleted student with ID {0}";, studentID); }

Creating the Client

  1. Open the Client application found at C:\WCFHandsOn\ChapterFive\Before.

  2. Add a reference to System.ServiceModel.

  3. Using Solution Explorer, add a reference to the StudentManagementRequest project.

  4. Using Solution Explorer, add a new class called StudentManagementProxy.cs.

    This class will contain the service interface and proxy code we will use to communicate to MSMQ.

    Add the following code, taking note of several key pieces:

    [View full width]

    using System.ServiceModel; using System.ServiceModel.Channels; namespace WCFHandsOn { [System.ServiceModel.ServiceContractAttribute(Namespace = "http://Microsoft .ServiceModel.Samples")] 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.Binding binding) : base(binding.Name.ToString()) { } public StudentManagementProxy(System.ServiceModel.EndpointAddress address, System .ServiceModel.Binding binding) : base(address.Uri.ToString(), binding.Name.ToString()) { } public void ProcessMessage(MsmqMessage<StudentManagementRequest> msg) { base.InnerProxy.ProcessMessage(msg); } } }

    The service interface provides only a single operation, ProcessMessage. This may seem odd, because you're aware that there are Add, Update, and Delete operations on the client. For MSMQ integration, you define 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.

    It is not possible to map different MSMQ messages to operation contracts automatically. As a result, there can be only one operation contract. As seen earlier, the server determines which action to take based on the message label. Thus, as you will see with the ProcessMessage operation is called by the client, the StudentManagementRequest object will be populated and the operation to be performed will be identified by the Label property.

    Note

    It is possible to define more than one operation contract for the service; however, it requires additional effort because you must identify which header in the MSMQ message (correlationID, Label, and so on) can be used to dispatch.


  5. 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, Client">              </endpoint>         </client>         <bindings>             <msmqIntegrationBinding>                 <binding name="Binding2">                    <security mode="None" />                 </binding>             </msmqIntegrationBinding>         </bindings>      </system.serviceModel> </configuration>

  6. Modify the Program.cs file to include the following code:

    [View full width]

    static void Main(string[] args) { 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)) { 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; p.ProcessMessage(msg); //Commit the transaction tx.Complete(); } } }

    This code will generate a message and send it to the queue. Our service will extract the message and enter the data into the database.

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 WCF, 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 6.3.

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





Presenting Microsoft Communication Foundation. Hands-on.
Microsoft Windows Communication Foundation: Hands-on
ISBN: 0672328771
EAN: 2147483647
Year: 2006
Pages: 132

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net