Hands-On Lab
Windows Server AppFabric Hosting:
Lab 02 - Introducing WCF Services
Lab version:1.0.0
Last updated:9/24/2018
||
Contents
Overview
Starting Materials
Exercise 1: Building the Order State Service
Task 1 –Creating the Project
Task 2 –Adding Custom User Event Traces
Summary
Overview
In this lab, you will build a WCF Service that is fundamental to the Fourth Coffee scenario: the OrderStateService. This service is responsible for keeping track of the order and their status. The service stores the orders in memory.With minor modifications, it couldinstead write to a durable store such as a database.
Objectives
In this lab you will become familiar with:
- Creating a new WCF Service project
- Taking a contract first approach to defining a service
- Hosting and monitoring a code based WCF Service with AppFabric
- Testing a service with the WCF Test Client
Setup
You must perform the following steps to prepare your computer for this lab:
- Complete the Development Environment Setup lab.
- Read through the Fourth Coffee Solution Overview to understand the solution that will be implemented in these labs.
- To simplify the process of registering the numerous Fourth Coffee applications with IIS, we haveprovided a utility called LabStarter that you should run as the first step in any lab.
- To use it, run LabStarter.exe from the %InstallFolder%\Assets directory and click the button corresponding to the Lab exercise you wish to open. This will perform the requisite configuration and then open the desired solution in Visual Studio for you automatically
Exercises
This Hands-On Lab comprises the following exercises:
- Building the Order State Service
Estimated time to complete this lab: 45minutes.
Starting Materials
Use the LabStarter as described in the Setup section to begin this lab.
Note:Inside each exercise folder, you will find anend folder containing a solution with the completed lab exercise.
Exercise 1: Building the Order State Service
In this exercise you will take a contract first approach to defining the OrderStateService. In a contract first approach, you define your service’s interface (namely the operations it will support) and then separately implement the functionality of that service.
Task 1 –Creating the Project
In this task,you add a new WCF Service project called OrderStateService to the FourthCoffee solution.
- Open the starter solution for this exercise as described in the Starting Materials section.
- In Solution Explorer, right click the FourthCoffee solution and select Add->New Project…
- In the dialog that appears, select WCF Service Application and for the name enter OrderStateService. Click OK.
- Next, delete the *.SVC and *.SVC.CS added by the project template. You will add your own in a moment.
- To the OrderStateService project, add a project reference to the Entities project. The Entities project contains the data types we will use in passing data into and out of this service (for example, it defines an Order, a Customer, etc.). These types have already been decorated with the appropriate DataContract / DataMember attributes so that they can be used in the SOAP message exchange.
- Right click the OrderStateService project and choose Add->New Item…
- In the dialog that appears, from the Web group, select the WCF Service item template. Name it OrderStateService.svc and click OK.
- Open the newly added IOrderStateService. This file will store the service contract (interface) that defines what operation the service will expose to clients.
- Replace the contents of this file with the following. You can use the provided code snippet for this. In the editor click where you want to insert the code and type the shortcut IOrderStateService and hit tab, or right click, select Insert Snippets… In the drop down select My Code Snippets and then the item with the aforementioned shortcut in its name).
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using FourthCoffee.Entities;
namespace OrderStateService
{
[ServiceContract]
publicinterfaceIOrderStateService
{
[OperationContract]
void AddOrder(OrderItem item);
[OperationContract]
void UpdateStatus(string orderId, string status);
[OperationContract]
ListOrderItem> GetOrderItems();
[OperationContract]
OrderItem GetOrderItem(string orderId);
}
}
- Observe that the OrderStateService defines four service operations (AddOrder, UpdateStatus, GetOrderItems and GetOrderItem). A service interface must be decorated with the ServiceContract attribute and each operation that will be callable must have an OperationContract decoration. You will implement the functionality for these operations next.
- Open the OrderStateService.svc.cs (referred to as the Code Behind for the service). This file will contain the actual service implementation.
- Replace the contents of the Code Behind with the following. You can use the provided code snippet for this. In the editor click where you want to insert the code and type the shortcut OrderMemoryStateService and hit tab, or right click, select Insert Snippets… In the drop down select My Code Snippets and then the item with the aforementioned shortcut in its name).
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using FourthCoffee.Entities;
using System.IO;
using System.ServiceModel.Web;
namespace OrderStateService
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
publicclassOrderMemoryStateService : IOrderStateService
{
Dictionarystring, OrderItem> m_orderItems = newDictionarystring, OrderItem>();
publicvoid AddOrder(FourthCoffee.Entities.OrderItem item)
{
m_orderItems.Add(item.OrderInfo.OrderId, item);
}
publicvoid UpdateStatus(string orderId, string status)
{
if (!m_orderItems.ContainsKey(orderId))
{
thrownewFaultException(
"Cannot update status. Order id invalid.");
}
m_orderItems[orderId].Status = status;
}
public FourthCoffee.Entities.OrderItem GetOrderItem(string orderId)
{
if (!m_orderItems.ContainsKey(orderId))
{
thrownewFaultException("Order id invalid.");
}
return m_orderItems[orderId];
}
publicList<FourthCoffee.Entities.OrderItem> GetOrderItems()
{
ListOrderItem> list = m_orderItems.Values.ToList<OrderItem>();
return list;
}
}
}
- There are a few things to note about the above code sample. First, the OrderMemoryStateService has been decorated with a ServiceBehavior that configures the InstanceContextMode mode to Singleton. This means that any and all requests to the OrderStateService interact with only the single instance that keeps the orders in memory. Second, the operations Add and Update work against the local dictionary m_orderItems which actually stores the orders. Finally, when there are exception cases, we throw a FaultException instead of a regular exception.
- Open the OrderStateService.svc (right click on the file and select View->Markup). This is referred to as the service endpoint, and it ultimately represents the terminal to which clients communicate, and maps those requests to an instance of the OrderStateMemoryService via the Service attribute (which identifies the service type by its namespace.classname) and the CodeBehind attribute (which specifies where this type is defined). Update the service attribute value to be “OrderStateService.OrderMemoryStateService” instead of “OrderStateService.OrderStateService” so that it uses the class you previously pasted in.
- Your service is now complete, however in order to run it within AppFabric requires an additional step.
- Open the Project Properties for the OrderStateService and select the Web tab. Scroll down to the Servers section, and choose the Local IIS Web Server radio option. Click the Create Virtual Directory button. By performing this configuration, the service will run within your local IIS, and be able to leverage AppFabric’s featurset. Without performing this configuration, the project would default to running with the Visual Studio Development Server, which does not contain the AppFabric components.
- In Solution Explorer, right click OrderStateService.svc and select ‘Set as Start Page’. This will ensure that when F5 is pressed, the WCF Test Client will automatically connect to the SVC endpoint (shown in a moment).
- Also in Solution Explorer, set the OrderStateService as the Startup Project for the solution.
- Proceed with Exercise 1, Verification 1 to make sure your service is working before continuing.
Verification 1
In this verification, you run the OrderStateService using Visual Studio and test it using the WCF Test Client.
- With the solution open, press F5.
- Visual Studio should start debugging. The WCF Test Client should load and display the service similar to the following:
- Double click the AddOrder entry (the one under IOrderStateService (BasicHttpBinding)).
- In the AddOrder tab, click within the Value column for the OrderInfo item until you get a drop down list. Select the Fourth Coffee entry as shown:
- Expand the OrderInfo object by clicking the arrow to the left of the OrderInfo text in the Name field.
- For the OrderId enter “abc123.”
- Click the Invoke button. The Response should return without error as follows:
- Next, check that your order was properly stored. Double click the GetOrderItems in the tree view.
- Click Invoke. Observe in the results the item you just ordered:
Task 2 –Adding Custom User Event Traces
- Now that you have a working service, let’s enhance it to output more detailed information to AppFabric. We will accomplish this by emitting user-defined events from the service.
- Add a class file called WCFUserEventProvider.cs to the OrderStateService project. This class will define a helper class that makes it easy to emit user events.
- Replace the contents of this class file with the following (this helper class is not a part of the .NET Framework, but is provided as a sample). You can use the provided code snippet for this. In the editor click where you want to insert the code and type the shortcut WCFUserEventProvider and hit tab, or right click, select Insert Snippets… In the drop down select My Code Snippets and then the item with the aforementioned shortcut in its name).
C#
//------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------
using System;
using System.Configuration;
using System.Diagnostics.Eventing;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Configuration;
using System.Web.Configuration;
using System.Web.Hosting;
namespace OrderStateService
{
publicclassWCFUserEventProvider
{
conststring DiagnosticsConfigSectionName = "system.serviceModel/diagnostics";
constint ErrorEventId = 301;
constint WarningEventId = 302;
constint InfoEventId = 303;
constint Version = 0;
constint Task = 0;
constint Opcode = 0;
constlong Keywords = (long)0x20000000001e0004;
constbyte Channel = 0x12;
constint ErrorLevel = 0x2;
constint WarningLevel = 0x3;
constint InfoLevel = 0x4;
EventDescriptor errorDescriptor;
EventDescriptor warningDescriptor;
EventDescriptor infoDescriptor;
bool hostReferenceIsComplete;
string hostReference;
privateString HostReference
{
get
{
if (hostReferenceIsComplete == false)
{
CreateHostReference();
}
return hostReference;
}
}
EventProvider innerEventProvider;
public WCFUserEventProvider()
{
Guid providerId;
if (HostingEnvironment.IsHosted)
{
DiagnosticSection config = (DiagnosticSection)WebConfigurationManager.GetSection(DiagnosticsConfigSectionName);
providerId = newGuid(config.EtwProviderId);
hostReferenceIsComplete = false;
}
else
{
DiagnosticSection config = (DiagnosticSection)ConfigurationManager.GetSection(DiagnosticsConfigSectionName);
providerId = newGuid(config.EtwProviderId);
hostReference = string.Empty;
hostReferenceIsComplete = true;
}
innerEventProvider = newEventProvider(providerId);
errorDescriptor = newEventDescriptor(ErrorEventId, Version, Channel, ErrorLevel, Opcode, Task, Keywords);
warningDescriptor = newEventDescriptor(WarningEventId, Version, Channel, WarningLevel, Opcode, Task, Keywords);
infoDescriptor = newEventDescriptor(InfoEventId, Version, Channel, InfoLevel, Opcode, Task, Keywords);
}
publicbool WriteErrorEvent(string name, string payload)
{
if (!innerEventProvider.IsEnabled(errorDescriptor.Level, errorDescriptor.Keywords))
{
returntrue;
}
return innerEventProvider.WriteEvent(ref errorDescriptor, name, HostReference, payload);
}
publicbool WriteWarningEvent(string name, string payload)
{
if (!innerEventProvider.IsEnabled(warningDescriptor.Level, warningDescriptor.Keywords))
{
returntrue;
}
return innerEventProvider.WriteEvent(ref warningDescriptor, name, HostReference, payload);
}
publicbool WriteInformationEvent(string name, string payload)
{
if (!innerEventProvider.IsEnabled(infoDescriptor.Level, infoDescriptor.Keywords))
{
returntrue;
}
return innerEventProvider.WriteEvent(ref infoDescriptor, name, HostReference, payload);
}
privatevoid CreateHostReference()
{
if (OperationContext.Current != null)
{
ServiceHostBase serviceHostBase = OperationContext.Current.Host;
VirtualPathExtension virtualPathExtension = serviceHostBase.Extensions.Find<VirtualPathExtension>();
if (virtualPathExtension != null& virtualPathExtension.VirtualPath != null)
{
// HostReference Format
// <SiteName<ApplicationVirtualPath>|<ServiceVirtualPath>|<ServiceName>
string serviceName = serviceHostBase.Description.Name;
string applicationVirtualPath = HostingEnvironment.ApplicationVirtualPath;
string serviceVirtualPath = virtualPathExtension.VirtualPath.Replace("~", string.Empty);
hostReference = string.Format("{0}{1}|{2}|{3}", HostingEnvironment.SiteName, applicationVirtualPath, serviceVirtualPath, serviceName);
hostReferenceIsComplete = true;
return;
}
}
// If the entire host reference is not available, fall back to site name and app virtual path. This will happen
// if you try to emit a trace from outside an operation (e.g. startup) before an in operation trace has been emitted.
hostReference = string.Format("{0}{1}", HostingEnvironment.SiteName, HostingEnvironment.ApplicationVirtualPath);
}
}
}
- Open OrderStateService.svc.cs.
- Just inside the class definition, declare the eventWriter instance that will be used to write user events.
C#
staticWCFUserEventProvider eventWriter = newWCFUserEventProvider();
- Next, you will instrument each of the operations to capture custom data to the AppFabric Tracked Events. To the end of the AddOrder method, insert the following bolded line which will capture a user event at the information level with the event type of “AddOrder” and the payload string specified in the second parameter. You can use the provided code snippet for this. In the editor click where you want to insert the code and type the shortcut WriteInfoAddOrder and hit tab, or right click, select Insert Snippets… In the drop down select My Code Snippets and then the item with the aforementioned shortcut in its name).
C#
publicvoid AddOrder(FourthCoffee.Entities.OrderItem item)
{
m_orderItems.Add(item.OrderInfo.OrderId, item);
eventWriter.WriteInformationEvent("AddOrder",
String.Format("Order Added. Order id = {0}. Count is now {1}.",
item.OrderInfo.OrderId, m_orderItems.Count));
}
- Similarly, modify the UpdateStatus method so it looks as follows after adding the lines in bold, adding a trace that captures an exception and one that captures an informational message. You can use the provided code snippet for this. In the editor click where you want to insert the code and type the shortcut WriteErrorUpdateStatus or WriteInfoUpdateStatus respectively and hit tab, or right click, select Insert Snippets… In the drop down select My Code Snippets and then the item with the aforementioned shortcut in its name).
C#
publicvoid UpdateStatus(string orderId, string status)
{
if (!m_orderItems.ContainsKey(orderId))
{
eventWriter.WriteErrorEvent("UpdateStatus",
String.Format("UpdateStatus() failed for invalid order id = {0}",
orderId));
thrownewFaultException("Cannot update status. Order id invalid.");
}
m_orderItems[orderId].Status = status;
eventWriter.WriteInformationEvent("UpdateStatus",
String.Format("Order Status Updated. Order id {0}. New Status is '{1}'",
orderId, status));
}
- Next, modify GetOrderItem by adding the two traces in bold. You can use the provided code snippet for this. In the editor click where you want to insert the code and type the shortcut WriteErrorGetOrderItem or WriteInfoGetOrderItem respectively and hit tab, or right click, select Insert Snippets… In the drop down select My Code Snippets and then the item with the aforementioned shortcut in its name).
C#
public FourthCoffee.Entities.OrderItem GetOrderItem(string orderId)
{
if (!m_orderItems.ContainsKey(orderId))
{
eventWriter.WriteErrorEvent("GetOrderItem",
String.Format("Invalid order id = {0}", orderId));
thrownewFaultException("Order id invalid.");
}
eventWriter.WriteInformationEvent("GetOrderItem",
String.Format("Got Order Item. Order id {0}. Status is '{1}'",
orderId, m_orderItems[orderId].Status));
return m_orderItems[orderId];
}
- Modify GetOrderItems by adding the line in bold. You can use the provided code snippet for this. In the editor click where you want to insert the code and type the shortcut WriteInfoGetOrderItemsand hit tab, or right click, select Insert Snippets… In the drop down select My Code Snippets and then the item with the aforementioned shortcut in its name).
C#
publicList<FourthCoffee.Entities.OrderItem> GetOrderItems()
{
ListOrderItem> list = m_orderItems.Values.ToList<OrderItem>();
eventWriter.WriteInformationEvent("GetOrderItems",
String.Format("Returning {0} items.", list.Count));
return list;
}
- Verify your work by completing Exercise 1 Verification 2.
Exercise 1 Verification
Verification 2
In this verification, you test the OrderStateService using the WCF Test Client as in Verification 1, but also examine the additional detail logged by the custom user events in AppFabric.
Note: Information Events such as AddOrder do not appear in the tracked events by default. If you are interested in examining User defined events emitted at the information level, you will need to ensure that your monitoring level is set to Troubleshooting. To do this, within IIS Manager, right click the Server node in the connections pane. Select Manage WCF and WF->Configure… In the dialog that appears, click the Monitoring tab. Within that pane, drag the Level slider all the way to the top as shown and click OK:
- With the solution open, press F5.
- With the WCF Test Client open submit a new order. Double click AddOrder and add an order with the OrderId of “xyz456” (without quotes) and a Status of “InProgress” Click Invoke.
- Next, you will update the status for an order that doesn’t exist. Double click UpdateStatus and for the OrderId enter “foobar” and give it a Status of “OrderCompleted”. Click Invoke. You should get an exception as shown:
- Now examine the results of these two calls in AppFabric. Open IIS Manager, expand the Connections tree on the left and select the OrderStateService node.
- Double click the Dashboard feature.
- The WCF Call History of the Dashboard should appear similar to the following (note that you have the two completed calls as expected, and 1 User Defined Error):
- Click the OrderStateService.svc link under Completed Calls. You will see the two calls you just made (one to AddOrder and the other made for you by the WCF Test Client when it requested the service’s metadata via a Get operation):
- Right click on the AddOrder call, and select View All Related Events. In the listing you should see the custom user event emitted for the added order. The value of the payload is visible the bottom pane (if you do not see this entry, your monitoring level may be set to the default of Health Monitoring, see the note above for instructions on correcting this) :
- Hit the Back button in the top left twice to return to the Dashboard.
- This time, click on the User Defined Errors link under the Errors section of the WCF Call History band, and in the listing that appears select the one event. Note that the Payload value now appears in the Errors tab:
Summary