Share via


Service Station

Developing .NET Web Services with Beta 2

Aaron Skonnard

Contents

Framework Evolution
Visual Studio 2005
XML Editor and Code Snippets
Whitehorse Designers
Interface-Based Service Contracts
More Core XSD/WSDL Improvements
Improved Standards Support, Interoperability
Wrapping It Up

Version 2.0 of the Microsoft® .NET Framework makes numerous improvements at various levels in the Web services protocol stack. In addition, better tool support and an increased focus on interoperability make your life easier. In this column I'll walk you through several of the major Web services tool improvements in Visual Studio® 2005 Beta 2, and introduce you to a new interface-based approach to defining service contracts. Then I'll discuss improved interoperability results across frameworks. But first, let me briefly explain how these changes mesh with what's going on in the Web Services Enhancements (WSE) and "Indigo" spaces.

Indigo is the code name for the upcoming set of .NET technologies for building and running connected systems that will be part of the next version of Windows and available as an add-on to Windows® XP. Note that, as usual, since this column discusses products that have not yet been released, implementation details may change in the future.

Framework Evolution

You can think of WSE as its own stack because you install it separately from the .NET Framework and you can use it as a standalone (you don't need ASMX to use WSE, and vice versa). You can, however, use WSE to enhance the behavior of ASMX with support for the WS-* implementations, giving you a hybrid of the two stacks, which happens to be the sweet spot today. Service Pack 2 for WSE 2.0 was recently released and is downloadable from the MSDN® Web Services Developer Center.

The choice between these stacks can be confusing for many developers. The guidance from Microsoft is to use ASMX by default, and to enhance ASMX with WSE 2.0 when you need message-level security. When you take this approach, you're still using the ASMX programming model; you're simply introducing security functionality through the ASMX SoapExtension framework (think pre- and post-processing), and when using policy, without making changes to your code (see Figure 1).

Figure 1 Evolution of Web Services Frameworks

Figure 1** Evolution of Web Services Frameworks **

Why does Microsoft offer this guidance? The ASMX programming model is more likely to migrate cleanly to Indigo than the alternative WSE 2.0 messaging API will be. You may still find it strategic and beneficial to leverage the WSE 2.0 messaging API in your services today (to use the TCP transport or to do custom hosting, for example), just as long as you're prepared to migrate the code when Indigo becomes available. The primary Indigo programming model is going to be very similar to the ASMX model.

WSE 2.0 will continue to be used to enhance ASMX 2.0 with security and other WS-* implementations it might offer. Unfortunately, WSE 2.0 is not currently compatible with the .NET Framework 2.0 Betas—they should be compatible, however, by the time version 2.0 ships. Also, it's likely that the next version of WSE (version 3.0) will have full ASMX integration, as illustrated in Figure 1, simplifying future migration even more.

Visual Studio 2005

Let's start by looking at some of the new tool features. One of the improvements that you'll notice as soon as you begin developing Web services with Visual Studio 2005 is the move away from the dreaded "Web project" to the new "Web site" model.

Web projects required you to have a virtual directory in place before you could even open the project. This complicated matters when moving projects around, changing directories, or modifying virtual directory names, and so on. With the Web site model, you can simply point Visual Studio to a physical directory on disk and it will use its own Web server (based on the Cassini Web server from Microsoft) to serve up files in that directory over HTTP. This lets you browse to and test ASMX files without having an IIS virtual directory. You can also open an IIS Web site by pointing Visual Studio 2005 to the IIS virtual directory (just like you could with earlier versions).

The new ASP.NET compilation model also simplifies ASMX development. Instead of having to compile your ASMX code into the /bin directory, you can now place your ASMX source files in the /Code directory and they'll be automatically compiled for you. In addition to compiling source files found in the /Code directory, ASP.NET 2.0 also uses custom build providers to process .xsd and .wsdl files placed there. It generates DataSet classes for the .xsd files, which is equivalent to running xsd.exe/DataSet, and proxy classes for each .wsdl file, which is also equivalent to running wsdl.exe.

One of the cool things about this architecture is that you can write custom build providers that plug into the build process, an encouraging environment for third-party tool support. Visual Studio 2005 has impressive support for the /Code directory. When you drop .xsd and .wsdl files in the /Code directory from within the IDE, it automatically makes the generated classes available to you via the Object Browser and IntelliSense®.

Visual Studio .2005 also gives you the option to embed code within the .asmx file or in a codebehind file. And with both techniques you still get IntelliSense and syntax highlighting unlike before where you had no choice but to use codebehind.

XML Editor and Code Snippets

In addition to these core IDE improvements, Visual Studio 2005 also comes with a greatly improved XML editor that fully supports XSD-driven IntelliSense. The new editor is truly amazing. It will automatically provide IntelliSense for the schemas found in C:\Program Files\Microsoft Visual Studio 8\Xml\Schemas or those found in your project. Numerous schemas ship with Visual Studio 2005 including the schemas for XML Schema Definition (XSD), Web Services Description Language (WSDL), SOAP 1.1, SOAP 1.2, XML Signature, XSLT, XHTML, .NET Framework 2.0 configuration files, MSBuild, and code snippets, to name a few. This means you now get IntelliSense when authoring any of these file types—and it actually works correctly, even with namespace scopes and XSD sequencing, and so on.

Visual Studio 2005 Beta 2 will also support XML code snippets and expansions just like the ones for C# or Visual Basic® that shipped in Beta 1. You can define your own XML code snippets or expansions that you commonly use (with the provided schema), manage them using the Code Snippet Manager, and then use them to automate authoring common XML fragments. This is especially interesting in the area of XSD and WSDL, both of which are hard languages to work with unassisted. Microsoft is planning to ship Visual Studio 2005 Beta 2 with numerous XSD and WSDL snippets and expansions for the most common authoring tasks.

For example, typing "oneOpPortType" followed by Tab expands into the following <wsdl:portType> fragment. You simply tab through the highlighted slots and enter the appropriate values, without having to worry about typing in all the angle brackets:

<wsdl:portType name="<span class="clsRed" xmlns="https://www.w3.org/1999/xhtml">name</span>" > <wsdl:operation name="<span class="clsRed" xmlns="https://www.w3.org/1999/xhtml">opname</span>" > <wsdl:input message="<span class="clsRed" xmlns="https://www.w3.org/1999/xhtml">inputMsg</span>" /> <wsdl:output message="<span class="clsRed" xmlns="https://www.w3.org/1999/xhtml">outputMsg</span>" /> </wsdl:operation> $selection$$end$ </wsdl:portType>

These snippets and expansions should make hand-authoring XSD and WSDL more tractable for the average XML developer. Although they probably won't do much for those of you who are angle-bracket impaired, these enhancements will likely do a great deal of good if you're interested in contract-first development techniques. Check out the Code Snippet Manager to begin playing with the provided snippets and expansions.

Visual Studio 2005 still comes with a graphical XSD designer, but it's still missing the WSDL equivalent. Developers looking for a more user-friendly WSDL authoring tool will have to resort to a third party. However, Visual Studio 2005 does introduce a new application designer as part of the Visual Studio Team System components (code-named "Whitehorse") that comes with some Web service design support.

Whitehorse Designers

The Whitehorse Application Connection Designer (ACD) is a design tool for modeling the components of an application and the connections between them, where the "connections" represent service contracts. You open the ACD in Visual Studio 2005 by selecting File | New Project | Other Project Types | Visual Studio Solutions | Distributed System. You can use this designer for multiple tasks to model new Web service endpoints, to implement existing Web service contracts, or to consume external Web services that you don't control.

In order to model services with the designer, first you drag a Web service onto the design service and begin modeling the endpoint contract before ever writing a line of code. Although you're essentially modeling WSDL by doing this, I don't consider it a WSDL editor because the designer is very limited and restrictive in its approach to WSDL design. It works fine, however, for simple contract definitions.

You then drag other application types (such as ASP.NET or Windows Applications) onto the design surface and connect them to services. This establishes a relationship between the two components based on the service contract definition (see Figure 2).

Figure 2 ACD Designer

Figure 2** ACD Designer **

Once you've modeled the entire application, the ACD allows you to generate the skeleton projects for all of the applications, with all of the code needed to implement and consume the defined service contracts. For example, it generates the ASMX classes in the Web service projects (with all the required attributes) and it generates the client-side proxy classes in the consuming applications. If you go back and modify the contract, it will automatically keep all the code synchronized.

The ACD is the first Microsoft design tool to truly head in the direction of contract-first development, but it's still missing a few important features. It makes you model the Web service contract from a method perspective and doesn't let you drop down to design the message formats (XSD definitions) from within the same environment. You have to fight the tool a bit to use a true XSD-first approach. Future versions should improve on these deficiencies, but not until the next version of Visual Studio. Nevertheless, it's still a powerful design tool worth considering today. In a future column, I'll walk through a complete example of contract-first development using Whitehorse.

Interface-Based Service Contracts

Most of the improvements in the .NET Framework 2.0 Web services framework are related to XSD and WSDL support, improved runtime serialization, and related optimizations. One of the newest, and most compelling, features in this area is support for interface-based Web services contracts.

The motivation for this feature is clear. Most distributed systems developers are used to designing their communication contracts using interface definitions, perhaps in the Interface Definition Language (IDL), which can be shared among all parties that need to communicate. The interface definition defines the message formats that go on the wire. Developers can then implement the interfaces in class definitions, assured that their implementations will be able to speak with any implementation that supports the same interface definition.

Contracts in Web services are very much the same, but they're expressed using XSD and WSDL definitions between parties. ASMX 1.1 makes it possible to generate class definitions from a WSDL contract (using wsdl.exe /server), similar to what IDL developers used to do, but since Visual Studio .NET doesn't provide a WSDL contract designer, most developers just start by writing class definitions and derive the WSDL contract from the implementation at run time. This approach mixes the notions of contract and implementation, cluttering the implementation class with numerous WSDL-related attributes that most developers don't even understand. This is dangerous considering that they could innocently modify an attribute without understanding the repercussions to the contract.

When taking a code-first approach, it makes more sense to start by authoring the Web services contract in the form of an interface definition, in an attempt to factor out the contract details from the implementation code. Keeping the contract and implementation separate makes things much clearer to the developer. The interface definition can then be mapped directly to a WSDL portType/binding definition for sharing with other systems that need to use the same contract. Other developers working with the .NET Framework can simply use the interface definition in their code instead of going through the WSDL translation. When developers implement the interface, they're guaranteed to be able to speak with anyone else that implements either the same .NET interface or the translated WSDL portType/binding definition.

Let's look at an example. The following is a traditional .NET interface that models a few weather-related operations:

public interface IWeatherService { string GetForecast(string zip); string GetForecastByDate(string zip, DateTime forecastDate); }

This interface could be the starting point for the contract used by a weather service. In order to make it a Web services contract, you need to annotate the interface with the System.Web.Services attributes (such as [WebServiceBinding] or [WebMethod], for instance). These attributes are used to specify the WSDL portType and binding details (such as XML namespaces, actions values, SOAP styles, and so on), which are not expressible in an unattributed .NET class definition. Here's an example:

[WebServiceBinding(Namespace = "https://example.org/weather")] public interface IWeatherService { [WebMethod] string GetForecast(string zip); [WebMethod] string GetForecastByDate(string zip, DateTime forecastDate); }

You must use [WebServiceBinding] on the interface and [WebMethod] on each method you want to expose. You can also use [SoapDocumentMethod] to specify additional details per method, but it's not required. Notice also that I didn't use the [WebService] attribute here. [WebService] must be used on the implementation class, not the interface.

With these attributes in place, there's enough information in the interface definition to generate a WSDL contract (portType/binding combination) that other parties can use. You can implement the WSDL contract by simply implementing the .NET interface in a class definition, as illustrated in Figure 3.

Figure 3 Implement WSDL Contract

[WebService(Namespace = "https://example.org/weather")] public class WeatherService : IWeatherService { #region IWeatherService Members public string GetForecast(string zip) { ... // implementation goes here } public string GetForecastByDate(string zip, DateTime forecastDate) { ... // implementation goes here } #endregion }

The only attribute you have to use on the implementation is [WebService], which fills in the <wsdl:service> details for the given endpoint. All of the WSDL portType/binding information is inherited directly from the base interface, making the implementation code much cleaner than before.

When using this approach, it's important to note that the interface completely controls the contract. You cannot mix and match attributes across the interface and class definitions. All contract-related attributes must be on the interface definition and you can't add attributes to the class that will result in a modification of the contract defined by the base interface. This means you shouldn't use the XML serialization attributes, [SoapDocumentMethod] or [WebMethod]'s MessageName property on the class when deriving from such an interface.

You can, however, use [WebMethod] on the class to specify runtime behaviors that don't affect the contract, such as BufferResponse, CacheDuration, Description, EnableSession, and TransactionOption. However, when doing so, you still need to use [WebMethod] on the interface definition.

If you don't meet these requirements, the ASMX behavior will be the same as in the .NET Framework 1.1 in which attributes defined on the interface will not show up in the service contract—instead they must all be defined on the class as before.

Support for interfaces is only available on the server, within the service implementation. When you generate proxies using wsdl.exe, it will always generate class definitions, not interfaces. However, wsdl.exe does come with a new command-line switch (/serverInterface) for generating server-side interface definitions (like the one shown in Figure 3) from a WSDL definition. It will generate one interface for each portType/binding combination. It just generates the .NET manifestations of the WSDL contract; it doesn't generate any class definitions (it leaves that part up to you).

Assuming you have an existing WSDL document named weather.wsdl, here's how you'd run the tool:

C:> wsdl.exe /serverInterface weather.wsdl

Then you simply add the generated interfaces to your project and implement them in class definitions. Although I believe /serverInterface will be the preferred method when starting with WSDL, wsdl.exe still supports the /server switch.

The /server switch generates an abstract base class instead of an interface, but as in the .NET Framework 1.1, the attributes are not inherited by derived classes. This approach doesn't provide you with a much better alternative since the attributes have to be manually copied to the derived class. The only difference with the version 2.0 implementation is that /server generates a partial class (another new feature), giving you a little more flexibility in designing your code. A partial class allows you to add helper code in a separate partial class file that will be merged during compilation, which is nice when using /server since it won't be overridden the next time you run wsdl.exe. Figure 4 shows an example of what the generated partial class looks like.

Figure 4 Generated Partial Class

[WebService(Namespace="https://example.org/weather")] [WebServiceBinding(Namespace="https://example.org/weather")] public abstract partial class WeatherService2 : WebService { /// <remarks/> [WebMethod()] [SoapDocumentMethod("https://example.org/weather/GetForecast", RequestNamespace="https://example.org/weather", ResponseNamespace="https://example.org/weather", Use=SoapBindingUse.Literal, ParameterStyle=SoapParameterStyle.Wrapped)] public abstract string GetForecast(string zip); /// <remarks/> [WebMethod()] [SoapDocumentMethod("https://example.org/weather/GetForecastByDate", RequestNamespace="https://example.org/weather", ResponseNamespace="https://example.org/weather", Use=SoapBindingUse.Literal, ParameterStyle=SoapParameterStyle.Wrapped)] public abstract string GetForecastByDate(string zip, DateTime forecastDate); }

Starting with WSDL and using /server or /serverInterface is considered the full-fledged contract-first model, which typically increases interoperability success when working across frameworks. The improvements made in this area make contract-first a more realistic possibility for many developers. The interface-based approach to defining service contracts is also very similar to the forthcoming Indigo programming model.

More Core XSD/WSDL Improvements

Most of the remaining changes revolve around improvements to core XSD and WSDL support. The underlying XSD serialization engine is now capable of handling a much wider range of XSD definitions. Before, when the serialization engine didn't understand a certain XSD construct, xsd.exe /classes would fail during code generation. Now, it simply maps unknown constructs to XmlElement fields that can be processed through the DOM API. The serialization engine is also able to support XSD sequencing through the [XmlElement(SequenceId=X)] attribute and the xsd.exe /sequence switch.

The wsdl.exe code generation engine has also been improved to provide a better developer experience. In addition to the /serverInterface addition, it also generates proxy classes that are easier to use, especially when performing asynchronous invocations. And since the code generator now produces properties instead of fields (by default), proxy classes can now be databound for use with form controls. Wsdl.exe also makes it easier to deal with real-world situations in which different proxy classes need to share the same XSD-derived classes (via the /shareTypes switch).

The core serialization engine has also been updated to deal with generics and SqlTypes in order to improve flexibility in code-first experience. Support for Nullable<T>, which maps to XSD's nillable='true', makes it possible to express nullable value types in .NET. This is especially valuable when consuming non-.NET WSDLs where this is common. However, generics are not used by the code generator when generating code from XSD or WSDL.

On top of these improvements, the .NET Framework 2.0 introduces additional flexibility via IXmlSerialiazable for custom serialization and schema importer extensions for custom code generation. It also promises improved performance by supporting compression between proxy and service instances, and by allowing you to pre-generate serialization assemblies using the new sgen.exe XML serialization support utility.

All of these improvements promise to increase interoperability and overall flexibility since the .NET Framework 2.0 can now deal with a wider range of XSD and WSDL documents found in the wild. For more information on these core changes, check out the MSDN Library article "New Features for Web Service Developers in Beta 1 of the .NET Framework 2.0,".

Improved Standards Support, Interoperability

The interoperability commitment from Microsoft is increasingly evident. For example, MSDN recently launched a new Web Services Interoperability site within the MSDN Web Services Developer Center. In conjunction with the new site, MSDN hosted an "Interoperability Month" packed full of webcasts focused on Web services interoperability topics and guidance on specific implementation scenarios. The Web services improvements in the .NET Framework 2.0 also substantiate the Microsoft commitment in this area. They have included support for two major industry standards: SOAP 1.2 and the WS-I Basic Profile(BP) 1.1.

SOAP 1.2 became a W3C Recommendation over a year and a half ago, and it still hasn't made its way into most mainstream Web services frameworks. Since not all frameworks will be able to move at the same time, committing to backward compatibility with SOAP is a must. When you implement an ASMX service in the .NET Framework 2.0, it will automatically support both SOAP 1.1 and SOAP 1.2 by default. Figure 5 shows what the corresponding section of machine.config looks like after installation.

Figure 5 Machine.config

<!-- machine.config --> <configuration> <system.web> <webServices> <protocols> <add name="HttpSoap1.2" /> <add name="HttpSoap" /> <!-- <add name="HttpPost" /> --> <!-- <add name="HttpGet" /> --> <add name="HttpPostLocalhost" /> <add name="Documentation" /> </protocols> </webServices> </system.web> ... </configuration>

You can control which protocols your services support by modifying the Web.config file for the given virtual directory (notice that the HttpPost and HttpGet are now commented out by default). Adding support for SOAP 1.2 in turn increases reach and interoperability since clients can now choose which protocol they want to use with your service.

Server-side support for SOAP 1.2 won't do much good unless it also exists on the client side. Hence, the .NET Framework 2.0 adds support on the client side to facilitate choosing the SOAP protocol version. You can use the /protocol switch to specify the default protocol to use when generating proxy classes using wsdl.exe (if you don't specify the protocol, it defaults to SOAP 1.1). For example, the following command line generates a proxy class that uses SOAP 1.2 by default:

C:> wsdl.exe /protocol:SOAP12 contract.wsdl

The generated proxy class contains an extra line in the constructor that sets the SoapVersion property of SoapHttpClientProtocol, as illustrated here:

[System.Web.Services.WebServiceBindingAttribute( Name="POServiceSoap12", Namespace="https://example.org/po/")] public class POService : SoapHttpClientProtocol { public POService() { this.SoapVersion = SoapProtocolVersion.Soap12; this.Url = "https://localhost/purchasingsystem/poservice.asmx"; } ...

You can set this property at any time when using the proxy class. You can even switch between protocol versions (across different invocations) using the same proxy instance.

The other major interoperability improvement is support for the WS-I BP 1.1. This support is provided in several ways. First, ASMX services conform to the BP 1.1 by default—you have to add attributes and modify the default behavior to violate BP 1.1. However, most developers don't know what changes they can make without breaking conformance. So ASMX also lets you add a "conformance claim" to your implementation class via the [WebServiceBinding] attribute, as shown here:

[WebServiceBinding(ConformsTo=WsiProfiles.BasicProfile1_1, EmitConformanceClaims = true)] public class POService : System.Web.Services.WebService { [WebMethod] public string HelloWorld() { return "Hello World"; } }

This is actually the default template you get every time you add a new ASMX Web service to your project. The ConformsTo property states that this implementation is supposed to conform to BP 1.1. When you view the ASMX documentation page for this service, it will attempt to validate this claim. If it notices something in your implementation that violates BP 1.1, it will tell you about it in human-readable form. One way to violate BP 1.1 is to annotate HelloWorld with the [SoapRpcMethod] attribute, which enables SOAP's rpc/encoded style, as shown here:

[WebServiceBinding( ConformsTo=WsiProfiles.BasicProfile1_1, EmitConformanceClaims = true)] public class POService : System.Web.Services.WebService { [WebMethod] [SoapRpcMethod] public string HelloWorld() { return "Hello World"; } }

Since using the SOAP encoding violates BP 1.1, you'd see the page in Figure 6 when browsing to the ASMX page.

Figure 6 Displaying WS-I BP 1.1 Violations

Figure 6** Displaying WS-I BP 1.1 Violations **

When making conformance claims in your code, you may also want to emit those claims into the WSDL definition so that consumers know that you're BP 1.1 compliant. The EmitConformanceClaims property controls this. When set to "true", the conformance claim shows up in the WSDL document, as shown here:

<wsdl:binding name="POServiceSoap" type="tns:POServiceSoap"> <wsdl:documentation> <wsi:Claim conformsTo="https://ws-i.org/profiles/basic/1.1" xmlns:wsi="https://ws-i.org/schemas/conformanceClaim/" /> </wsdl:documentation> <soap:binding transport="https://schemas.xmlsoap.org/soap/http" style="document" /> <wsdl:operation name="HelloWorld">

With this in place, consumers are able to do their own analysis of your WSDL definition to confirm conformance. Wsdl.exe has been upgraded to do just this. When it sees a conformance claim, it will check conformance and display any problems it finds right away, helping you avoid interoperability problems further down the road.

Wrapping It Up

I've tried to give you a sneak peek at some of the new Web services features coming in the .NET Framework 2.0 Beta 2. Most of the changes are focused on polishing the existing framework with better tool support and more interoperable results. The outcome is a more mature and efficient Web services platform prepared for migration to Indigo.

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

Aaron Skonnard is a co-founder of Pluralsight, an education and content creation company, where he focuses on XML and Web services technologies. He's a contributing editor to MSDN Magazine and the author of several books, including Essential XML Quick Reference (Addison-Wesley, 2001).