Service Station

Web Service Software Factory Modeling Edition

Gerardo de Geest and Gerben van Loon

Code download available at: ServiceStation2008_Launch.exe(199 KB)

Contents

Data Contract Model
Service Contract Model
ASMX or WCF?
Host Model
Generating Code
Customizing the Service Factory
Enterprise Library Exception Shielding
Building the Extension
Using the Extension

Web Service Software Factory: Modeling Edition, also known as the Service Factory, is a collection of resources to help you model and build Web services for Windows® Communication Foundation (WCF) and ASMX in an easy and efficient way. The main difference from previous versions of the Service Factory is that this latest edition uses models whereas the previous release was based on wizards by way of the Guidance Automation Toolkit (GAT). The Service Factory now allows you to build a Web service by creating three different models: the data contract model, the service contract model, and the host model. We will first discuss these three models, and then we will show you how to customize the Service Factory. You can find additional information at the Web sites listed in the "Online Service Factory Resources" sidebar.

The main disadvantage of using wizards by way of GAT for doing code generation is that it complicates matters when you have to change something in the generated code. Suppose you want to rename an operation of a generated service—you have to either save your custom code and redo the entire wizard where you use the new name or rename the operation in the generated code. Redoing the wizard is time-consuming; since the wizard has no memory, you have to specify everything again. Changing the code generated by the wizard is not ideal either since you are back to working in the details while the goal of the wizard is to avoid this. Models provide a good solution to the problem because, in contrast to wizards, models are able to remember your input.

Building modeling languages that integrate into Visual Studio® was difficult in the past, but the Domain-Specific Language (DSL) Tools that ship with the Visual Studio SDK make it easier. The Service Factory now ships with three types of models built using the DSL Tools. Note that there are no models for data access and service security. The Data Access Guidance Package from the previous Service Factory release is now available as a separate factory called Repository Factory, and the Security Guidance Package is available as a separate package on the Service Factory community site.

The new version of the Service Factory also serves as a good example for building your own Software Factories since it is using and combining all the tools that are available to build one:

  • GAT for the wizards used to generate projects and provide context menus
  • DSL Tools for the three different model types
  • Designer Integration Service (DIS) to support cross-model references

The Service Factory source also contains some interesting libraries that have features the current factories tools do not have. It could be interesting to reuse these if you are building your own factory:

  • A code-generation library that allows you to generate code in separate files
  • A cross-model validation library to assist you in verifying cross-model references
  • A Visual Studio mock objects library to help you easily unit test your factory

As we mentioned, the Service Factory consists of three types of models. In the data contract model, the data contracts of the Web service are defined. In the service contract model, the service and its contracts, operations, and messages are defined. Finally, the host model is used to model endpoints of the services and client proxies.

To introduce the factory, we will create a Web service for ordering pizzas from Peedy's Pizza Palace. The Web service will take orders for pizzas, allowing you to choose among different sizes and different toppings. When the order is placed, the price of the order is returned to the customer as a confirmation.

Data Contract Model

In the data contract model, different kinds of data can be modeled together. In addition to defining basic types as contract elements, you can also define collections, enumerations, and fault contracts. Fault contracts can be returned when some error occurs in the service.

In the data contract model for Peedy's Pizza Palace, we have defined a PizzaOrder as a collection of pizzas that the customer would like to order. Every pizza has a size and a topping. This is modeled using enumerations. Furthermore, we have defined a fault contract, OutOfOrder, that is thrown in case a pizza is out of order, meaning that Peedy's Pizza Palace cannot deliver the pizza because it is missing some part of the topping. It is also possible to return values even when there is a fault contract. For example, in this case we show the price of the order to the user anyway. The complete data contract model is shown in Figure 1.

Figure 1 Data Contract Model for Peedy's Pizza Palace

Figure 1** Data Contract Model for Peedy's Pizza Palace **(Click the image for a larger view)

Service Contract Model

Data contracts are used in the service contract model. In this sample, the service contracts and operations of the Peedy's Pizza Palace Web service are modeled. The model (see Figure 2) is actually quite simple since we only need an operation to order pizzas.

Figure 2 Service Contract Model for Peedy's Pizza Palace

Figure 2** Service Contract Model for Peedy's Pizza Palace **(Click the image for a larger view)

Each operation in a service contract model can have a maximum of two message contracts associated with it: one message contract for the request message and one message contract for the response message. You can add parts to a message contract by right-clicking on it. A part can be either a primitive type, such as System.Decimal, or a data contract as defined in the data contract model. The DSL Model Element Selector, shown in Figure 3, shows all the data contracts that are available in the project.

Figure 3 Selecting the Data Contract

Figure 3** Selecting the Data Contract **

ASMX or WCF?

Before we move on to the host model, we must first create our solution structure. This is a set of projects in which the code for the Web services will be generated. It is at this point that we have to choose our implementation technology. It is important to note that in the Service Factory, two technologies are already available—ASMX and WCF—and it is easy to extend the Service Factory with your own service technology. For our demonstration, we will use WCF. In order to make this selection, we simply right-click on the solution, and the options shown in Figure 4 are displayed. We select WCF Implementation Projects at the bottom of the menu, and the Service Factory creates a couple of projects where the code generated from the models will be stored.

Figure 4 Create the WCF Solution Structure

Figure 4** Create the WCF Solution Structure **

Figure 5 shows the generated solution structure. Note that a variety of projects have been generated—this is to make the code easier to manage. We will get back to this solution structure in a bit, when we discuss the project mapping table.

Figure 5 Solution Structure for Peedy's Pizza Palace

Figure 5** Solution Structure for Peedy's Pizza Palace **

We also need to specify whether we want to use ASMX or WCF on the data contract model and on the service contract model. Each of these models has a property called Implementation Technology that can be set to ASMX, WCF, or your own custom implementation technology. When this property is set, many of the entities on the models get new properties that are specific to the chosen technology (see Figure 6a). As you can see in Figure 6b, after we've selected WCF as the implementation technology, a WCF settings category is added to the properties window. This lets us choose what kind of collection type the PizzaOrder collection should be.

Figure 6a Before Selecting a Technology

Figure 6a** Before Selecting a Technology **

Figure 6b After Selecting a Technology

Figure 6b** After Selecting a Technology **

Host Model

The host model is used to model the endpoints of the Web service and the client proxies. This model looks completely different from the other two models we've discussed previously. At this point, you can start modeling in the host explorer by right-clicking on elements, as opposed to simply dragging and dropping elements onto the design surface. By right-clicking on the host model element, you can add a new host and a new client, as shown in Figure 7.

Figure 7 Adding Clients and Hosts to the Host Model

Figure 7** Adding Clients and Hosts to the Host Model **

After adding a PizzaHost and a PizzaClient, the implementation technology for both can be selected. We have chosen to set both to WCF. It is also necessary to select an implementation project for both. Fortunately, the Service Factory has already generated those for us. As you can see, the Tests folder in the solution structure shown in Figure 5 contains the projects PizzaService.Host and PizzaService.Client. These can be used as implementation projects.

We create a new ServiceReference by right-clicking on the PizzaHost. For this new ServiceReference, we need to select a service implementation—this is a reference to our service model. We can select the implementation in the same way we selected data contracts in Figure 2.

For each ServiceReference you create, you can define different endpoints. We have created one endpoint, called PizzaEndpoint, and have configured it to use the wsHttpBinding.

Now that we have an endpoint, we can create a proxy for our client. To add a new proxy, we right-click on the PizzaClient in the Host Explorer. With a combobox in the property window of the proxy, we can select the endpoint. The completed host model is shown in Figure 8.

Figure 8 Completed Host Model for the Pizza Service

Figure 8** Completed Host Model for the Pizza Service **

Generating Code

Online Service Factory Resources

All the models we need for our pizza ordering service are now defined, and we are ready to generate the service. To do this, we first have to fill in the project mapping table field for each of our models—this can be defined in the property window of each model. When selecting this property, a selection window pops up. In our case, there is only one option: PizzaService. This is because all the roles were already defined when we created the solution structure. The roles can be found in the file projectmapping.xml, which is also part of our solution structure. In this file, a role can be associated with a project.

The next step is to actually generate the code. Right-clicking on the data contract model gives us the Generate Code option. Selecting this option generates the data contracts in the PizzaService.DataContracts project. The same can be done for the service contract model, which generates code in the PizzaService.MessageContracts, PizzaService.ServiceContracts, and PizzaService.ServiceImplementation projects.

Generating code for the host model is done in two steps: we generate code once for the host and once for the client. To generate code for the host, we click on the PizzaService ServiceReference and the Visual Studio designer presents a button called Validate Model. When we click the button, the model is validated correctly and we then are presented with the Generate Service button. Clicking this, as you've surely guessed by now, generates code in the PizzaService.Host project.

Now to generate the client, we first have to run the host, so we compile the PizzaService.Host project and run it in a Web browser. Next we click on the PizzaProxy in our host model and click generate code. This opens a wizard, shown in Figure 9, that allows us to configure the client. While the service address is filled in automatically by the Service Factory, we go ahead and define the security settings for the host. For instance, if an X.509 certificate is needed in order to connect to this service, we can add it to the client by using this wizard.

Figure 9 Wizard Used to Generate the Client

Figure 9** Wizard Used to Generate the Client **(Click the image for a larger view)

At this point, the service is ready to use and can be published in IIS. Business logic and entities for the service can be added to the PizzaService.BusinessEntities and PizzaService.BusinessLogic projects. The only other thing we need to do is create a new partial class, called PizzaServicePeedy, in the PizzaService.ServiceImplementation project. This is used to add business logic to the generated service implementation. Unfortunately, if we edit the generated code directly, our logic will be overwritten the next time we generate code. But with a partial class, this is not the case since the generator does not touch a file we have created ourselves.

To show this, we have defined a class in the PizzaService.BusinessLogic project called PriceCalculator with a method called CalculatePrice. (It's important to note that this is not a recommended practice—it's much better if the business logic doesn't "know" anything about the service, which is why there is a translation layer. To support this process, Service Factory has a translator built in that helps you translate your data contract into business entities. But while you may not want to do this in production, we're using this technique here as a simple way to illustrate a fundamental point.) This method takes a PizzaOrder data contract as a parameter and returns the price of the order. The only thing we need to do now is encapsulate this price in a MessageContract and then return the MessageContract. Therefore, to do this we create a partial class in the PizzaService.ServiceImplementation project called PizzaServicePeedy that overrides the method OrderPizza generated by the Service Factory. This partial class is shown in Figure 10.

Figure 10 PizzaServicePeedy Partial Class

namespace PizzaService.ServiceImplementation { public partial class PizzaServicePeedy { public override PizzaService.MessageContracts.OrderPizzaResp OrderPizza(PizzaService.MessageContracts.OrderPizzaReq request) { PizzaService.MessageContracts.OrderPizzaResp result = new PizzaService.MessageContracts.OrderPizzaResp(); result.price = PizzaService.BusinessLogic.PriceCalculator .CalculatePrice(request.order); return result; } } }

To test our service, we start the PizzaService.Host project. In the PizzaService.Client project, we can then define the following method to call the service:

static void Main() { PizzaOrder order = new PizzaOrder(); Pizza pizza = new Pizza(); pizza.PizzaSize= PizzaSize.Large; pizza.Topping = Topping.Hawaii; order.Add(pizza); PizzaContractClient proxy = new PizzaContractClient(); Console.WriteLine(proxy.OrderPizza(order)); proxy.Close(); }

When the program runs, the number 7 is printed to the output window of Visual Studio, as this is the price for every type of large pizza available at Peedy's.

Customizing the Service Factory

The Service Factory automates many things for you when building services. But what if you need to do things a little bit differently or you need to integrate these services with your existing application framework? Well, the full source code of the Service Factory is available on the CodePlex site (www.codeplex.com/servicefactory), which means you can customize the factory itself.

When you look at the Service Factory source, you'll find that it consists of the following parts:

Domain-Specific Languages Specifically, these include the data contract, host designer, and service contract. These projects specify how the modeling languages are constructed and visualized.

Extenders There are six extender projects. These projects specify the extra properties that are needed by the DSLs to support specific technologies (ASMX and WCF).

Service Factory Guidance Package This contains the solutions template and the custom context menus.

Libraries These contain the code-generation framework, validation framework, code-generation strategies, and the validation rules.

Model Project This custom project contains the models.

For this discussion, we will divide the customizations into two categories: those that require you to rebuild the source and those that don't require you to rebuild the source (extensions). These are outlined in Figure 11.

Figure 11 The Two Customization Categories

Must rebuild the factory
Changes Description
Modifying existing GAX (Guidance Animation eXtensions) recipes You must make changes to the Service Factory Guidance Package, and this, in turn, means you must rebuild your own factory.
Adding new project roles You must make changes to the Service Factory Guidance Package, and, therefore, you must rebuild your own factory.
Modifying solutions templates You must make changes to the Service Factory Guidance Package, and this, in turn, means you must rebuild your own factory.
Adding a new model You must recompile the factory since you need to make changes to the model project to support your new model.
Changing the modeling languages This requires changes to the existing DSL projects, and, therefore, you must rebuild your factory.
Do not need to rebuild the factory
Changes Description
Adding new solution templates You can simply define your solution templates in your own separate GAT guidance package.
Adding new GAX recipes You can define new GAX recipes, such as wizards and context menus, in your own GAT guidance package.
Adding new generation strategies You can add extra strategies in your own assembly using the ICodeGenerationStrategy interface from the libraries. (Technology extenders are able to use the assembly.)
Modifying text templates You can change the T4 text templates used to generate code if you want to change how code is generated. These are located in the service factory installation folder in the "Guidance Package\TextTemplates" subfolder.
Adding new model validation rules You can add extra validation rules to the ruleset.config in the Guidance Package folder of the service factory installation folder.
Adding new technology extensions or properties By default, the Service Factory ships with technology extenders for ASMX and WCF. You can add extra properties to these extenders or add your own extender in your assembly by deriving from ExtensionProviderBase. New or changed extenders can be dropped in the service factory installation folder in the "Guidance Package\Lib" subfolder.

In general, you should try to avoid customizations that require you to rebuild the factory and create a new installer. Aside from being more complex and potentially error-prone, these types of customizations also increase the probability of encountering problems when migrating to the next version of the factory or integrating with future Microsoft products. That said, the Service Factory source does include a setup project to help with the rebuild process. This project includes code-generation templates that generate WIX (Windows Installer XML), making the task easier and faster.

In the next section, we will show you how to add a property to the existing WCF extender and change the templates to generate extra code for the new property. It is beyond the scope of this column to examine the other extension points, so check out the Service Factory Extensions Hands-on Lab, available at codeplex.com/servicefactory.

Note that we are modifying the source of the factory, but we are only changing a single assembly, which is loaded dynamically. Thus, we are not rebuilding the factory.

Enterprise Library Exception Shielding

In services, it is common to prevent sending unknown exceptions to clients. The Exception Handling Application Block in Enterprise Library (EntLib) 3.1 can help by applying configured exception shielding policies to WCF services. This requires the ExceptionShielding attribute on a service with the name of the exception shielding policy from the Exception Handling block as a parameter.

In the Service Factory, the whole service class is generated so we can set the attribute on the generated code manually, but that will be overwritten whenever we have to regenerate code. A solution for this is to extend the service model with a setting for the EntLib Exception shielding attribute. Note that EntLib exception shielding only works with WCF services. Also, adding new properties to extenders doesn't require us to rebuild the whole factory.

Building the Extension

We are going to modify the source of the factory, but we're only making changes to a single assembly: the WCF service contract extender. The extender assembly is loaded dynamically, so we only need to build the changed extender. For building this extension, we assume that you have the Service Factory installed using the binary installers. You also need to install the source since we are going to make changes to a part of it. You can partially follow "Installation Instructions for Source Code Installer" from the Service Factory installation instructions. You have to skip the steps where you rebuild the entire solution or register the Guidance Package, since that will make changes to the original binary installed version.

We are going to add our new property to the WCF service contract extender, found in the ServiceFactory.Extenders.ServiceContract.Wcf project of the Service Factory Source. Just open this single project instead of the whole solution, since we only need to make changes to this project. The exception shielding setting is a property of a WCF service, so we can add a new property to the WCFService class (as shown in Figure 12).

Figure 12 Code for Extra Property

namespace Microsoft.Practices.ServiceFactory.Extenders.ServiceContract.Wcf { [Serializable] [CLSCompliant(false)] [ObjectExtender(typeof(Service))] public class WCFService : ObjectExtender<Service> { ... private string entLibWCFExceptionPolicy; [Category("Enterprise Library Integration"), Description("EntLib WCF Exception Shielding Policy"), DisplayName("EntLib Exception Shielding Policy"), ReadOnly(false), Browsable(true)] [XmlElement("EntLibWCFExceptionPolicy")] public string EntLibWCFExceptionPolicy { get { return entLibWCFExceptionPolicy; } set { entLibWCFExceptionPolicy = value; } } } }

We also need extra references to the EntLib DLLs when we're using the ExceptionShielding attribute. We can add extra assembly references to the ServiceLink class, and, in this way, the generated projects will automatically get these references:

[AssemblyReference("Microsoft.Practices.EnterpriseLibrary" + ".ExceptionHandling, Version=3.1.0.0, Culture=neutral," + "PublicKeyToken=b03f5f7f11d50a3a")] [AssemblyReference("Microsoft.Practices.EnterpriseLibrary" + ".ExceptionHandling.WCF, Version=3.1.0.0, Culture=neutral," + "PublicKeyToken=b03f5f7f11d50a3a")]

Now we have to change the code-generation templates to generate the extra code for the new property. The templates for generating WCF code are in the Guidance Package\TextTemplates\WCF\CS subfolder of the Service Factory installation folder.

We're going to add extra code to the ServiceImplementation.tt file. Save a copy of the original file first in case you ever want to switch back to the original template. At the bottom of the ServiceImplementation.tt file, we add the following method, which returns the EntLib exception policy only when a value is filled in:

private string GetEntLibExceptionShieldingPolicy( Service service) { WCFService wfcService = GetObjectExtender<WCFService>(service); if(string.IsNullOrEmpty( wfcService.EntLibWCFExceptionPolicy)) { return string.Empty; } else { return "[ExceptionShielding(\"" + wfcService.EntLibWCFExceptionPolicy + "\")]"; } }

To complete our extension, we also need to insert a using statement for the EntLib namespace and a call to the previously created method right above the base class definition, as shown here:

using System; using WCF = global::System.ServiceModel; using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF; namespace <#=CurrentExtender.ArtifactLink.Namespace#> { /// <summary> /// Service Class - <#= CurrentElement.Name #> /// </summary> [WCF::ServiceBehavior(Name = "<#= CurrentElement.Name #>", ... <#= GetEntLibExceptionShieldingPolicy(CurrentElement) #> public abstract class <#= CurrentElement.Name #>Base... ... }

Using the Extension

To use the extension, we only need to rebuild the changed ServiceFactory.Extenders.ServiceContract.Wcf project and make sure the installed service factory dynamically loads our changed WCF extender assembly. But we must warn you: do not rebuild the whole source code of the Service Factory at this point; this will make changes to the binary installed version. Only rebuild the extender project.

Again, it is a good practice to save a copy of the original extender. (The original WCF extender Microsoft.Practices.ServiceFactory.Extenders.ServiceContract.Wcf.dll can be found in the Guidance Package\Lib subfolder of the Service Factory installation folder.) Now we shut down all instances of Visual Studio and overwrite the extender DLL with our own version. Then when we restart Visual Studio and create a new service that uses WCF Extension, our new property pops up when the service is selected (see Figure 13).

Figure 13 New EntLib Property on the Service

Figure 13** New EntLib Property on the Service **(Click the image for a larger view)

When we fill in an exception policy and generate the code, the right EntLib assemblies are added to the generated project. In addition, the using statement and the EntLib attribute are added to the service implementation class. Of course, now our added attribute does not get lost when regenerating since we changed the code-generation template instead of the generated code. The new service code looks like this:

using System; using WCF = global::System.ServiceModel; using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF; namespace WCFService1.ServiceImplementation { /// <summary> /// Service Class - Service1 /// </summary> [WCF::ServiceBehavior(Name = "Service1", ... [ExceptionShielding("MyExceptionPolicy")] public abstract class Service1Base : WCFService1.ServiceContracts.IServiceContract1 { ... } }

In order to really get EntLib exception shielding working, we also have to configure an Exception Handling Application Block with an exception shielding policy. You can refer to the EntLib documentation for more details on creating exception handling policies.

For this extension, we added a property to the existing WCF extender, told the extender that we needed extra assembly references, changed the templates to generate the right code for the new property, and made the Service Factory use our changed WCF extender. The upcoming policy properties extension point will make these sorts of extensions easier by allowing you to specify new properties in separate assemblies. In this way, it won't be necessary to make changes to existing extenders.

Send your questions and comments to sstation@microsoft.com.

Gerardo de Geest is a Solution Developer at Avanade, specializing in software factories and domain-specific languages. He has been doing research into the evolution of domain-specific languages, especially within Microsoft Web Service Software Factory Modeling Edition. Gerardo can be contacted at gerardod@avanade.com.

Gerben van Loon is a Solution Developer at Avanade, specializing in software factories and domain-specific languages. He started using Microsoft DSL-Tools back with the early CTPs. Gerben can be contacted at gerbenva@avanade.com.