New Features for Web Service Developers in Beta 1 of the .NET Framework 2.0
Elliot Rapp
Yasser Shohoud
Matt Tavis
Microsoft Corporation
June 2004
Applies to:
Microsoft .NET Framework 2.0
Microsoft Visual Studio 2005
Summary: Check out the new improvements in productivity, performance, extensibility, and standards
support in Microsoft .NET Framework 2.0. (12 printed pages)
Introduction
The .NET Framework 2.0 Beta 1 release represents a significant milestone for the Web services and XML
Serialization stack in the .NET Framework. The enhancements in this release are centered on delivering
a mature platform that makes it even easier to develop connected systems with Web services.
Developers who have built Web services using previous versions of the Framework will be happy to find
this version delivers many of the features and enhancements they've asked for. These new features fall
into four categories:
1. Productivity Enhancements. There are a number of features that provide a better developer
experience when consuming Web services. For example, the new event-based asynchronous
programming model provides an intuitive way for invoking Web services asynchronously.
2. Extensibility. Many Web services developers want to take full control over the Web service
schema, the wire message, and the proxy types generated by wsdl.exe or Add Web Reference.
Developers will find what they're looking for in the new Schema Importer Extensions and the
improved IXmlSerializable interface.
3. Performance. Two new features lower client start-up time and network utilization, in addition to
other XML serialization performance improvements.
4. Commitment to Interoperability and Standards. Out-of-the-box support makes it even easier to
build Web services that conform to the WS-I Basic Profile 1.0. In addition, Version 2.0 of the .NET
Framework supports the W3C SOAP 1.2 standard.
Productivity Enhancements
Data Binding and Event-Based Asynchronous Programming
A major focus of our efforts in the .NET Framework 2.0 has been enhancing developer productivity. Our
goal is to make .NET the most productive platform for Web service application development. To that end,
we have listened to your feedback and introduced a suite of three new features that we hope will provide
an immediate improvement to your productivity. These features include enabling data binding to data
returned from a Web service, a new event-based asynchronous programming model, and type sharing
between services.
Many developers have asked for the ability to automatically bind to data returned from Web services.
Version 2.0 of the .NET Framework enables this scenario by generating properties on client proxy types
rather than fields making auto-generated proxy types suitable for data binding by default.
The second feature concerns the use of asynchronous method calls. The new event-based asynchronous
programming model simplifies the task of invoking a Web service asynchronously. In addition to using an
event paradigm instead of BeginInvoke/EndInvoke, the new programming model also takes care of
thread synchronization automatically so that event handlers are invoked on the same thread that made
the asynchronous call. Here is a code example that shows how to use the new event-based asynchronous
programming model. The SearchCompleted function also shows how to bind the returned data to a UI
data grid.
private void btnSearch_Click(object sender,
EventArgs e)
{
//application code omitted for brevity
...
//hookup async event handler
proxy.SearchCompleted +=
new SearchCompletedEventHandler(this.SearchCompleted);
//call the Search method asynchronously
proxy.SearchAsync(criteria, currentSearchToken);
btnCancel.Enabled = true;
}
void SearchCompleted(object sender, SearchCompletedEventArgs args)
{
//bind returned results to the UI data grid
gridSongs.DataSource = args.Result;
}
private void btnCancel_Click(object sender, EventArgs e)
{
//by clicking on the Cancel button users
//can cancel the asynchronous search operation
proxy.CancelAsync(currentSearchToken);
}
Type Sharing
In many real-world scenarios, you may want to factor your application's functionality into individual
services that group methods that logically fit together. This typically leads to sharing one or more data
types between those Web services. For example, you may have an Order Entry service that returns an
Order object and an Order Status service that takes in an Order object. Today, a client that wants to
consume both of these services ends up with two different Order classes, making it cumbersome to take
the output of the first service and pass it to the second.
Version 2.0 of the .NET Framework introduces a feature that provides the client with one Order class
that's shared by the two service proxies. This feature is exposed on wsdl.exe with the /sharetypes
switch. When using this switch you supply the URLs of two or more WSDL documents on the command
line. For example:
wsdl.exe /sharetypes http://localhost/webservice1.asmx?wsdl http://localhost/webservice2.asmx?wsdl
http://localhost/webservice3.asmx?wsdl
The code generation engine recognizes when types are equivalent based on their names and namespaces,
and by comparing their Schema definition.
Extensibility
The goal of any good infrastructure component is to be invisible until needed and provide an extensibility
model for custom scenarios. In most Web services scenarios the XmlSerializer meets these
requirements. However, recent customer feedback and more advanced Web services usage scenarios
have made apparent the need for greater extensibility in the serialization infrastructure. To that end,
the .NET Framework 2.0 introduces two major new features that provide you with more control for
advanced serialization and code generation capabilities:
• Full support of imperative serialization using IXmlSerializable
• Custom proxy code generation through Schema Importer Extensions
Imperative serialization using IXmlSerializable
The IXmlSerializable interface has been in the .NET Framework since version 1.0, but was not intended
for general usage. This interface was designed specifically for System.Data.DataSet to enable custom
serialization control and was marked for internal use to discourage its usage. Full support for
IXmlSerializable in ASP.NET Web services has been introduced in the .NET Framework 2.0 in direct
response to customer feedback for scenarios that require more control of the schema and wire format.
Now developers can control both the representation of their types on the wire as well as the schema that
is generated by the Web service. To remain consistent with the existing interface, the methods and
signatures have been left unaltered but the IXmlSerializable.GetSchema method should no longer be
used. A new attribute, [XmlSchemaProvider], has been introduced to indicate the static method on
the type that generates and inserts the schema in the XmlSchemaSet for the service.
The ReadXml method controls reading the serialized format and is handed an XmlReader to read from
the stream and populate the type. The WriteXml method controls writing the serialized format and is
handed an XmlWriter to write out the data to the stream.
An example where you might need this type of control is for streaming large amounts of data. To enable
streaming of data, you have to turn off response buffering and then chunk the data into discrete blocks
of data demarcated with XML elements. IXmlSerializable lets you control the schema for this chunking
format and control the reading and writing of this data to the stream with the ReadXml and WriteXml
methods.
The following example shows a class implementing IXmlSerializable:
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
//application code omitted for brevity
...
//This is the new XmlSchemaProviderAttribute
[XmlSchemaProvider("GetMySchema")]
public class SongStream : IXmlSerializable
{
private const string ns =
"http://demos.teched2004.com/webservices";
private string filePath;
public SongStream()
{
//default constructor for serializer
}
public static XmlQualifiedName GetMySchema(XmlSchemaSet xs)
{
//code omitted for brevity
//Here you want to add your type's schema to xs
//and returned your type's QName
}
void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer)
{
//code omitted for brevity
//Here you want to write out the serialized instance
}
System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema()
{
// GetSchema on the IXmlSerializable interface is not used.
return null;
}
void IXmlSerializable.ReadXml(System.Xml.XmlReader reader)
{
//code omitted for brevity
//Here you want to deserialize instance data from the
reader
}
}
By implementing IxmlSerializable, you get full control over both the Schema representation and wire
format of your type.
Schema Importer Extensions
In addition to runtime extensibility provided by IXmlSerializable, there is also a need for greater
design-time control. When using Add Web Reference or wsdl.exe, the Web services infrastructure
interprets the WSDL of the target service and generates proxy classes to interact with that service.
Customer feedback has indicated the need for even further control. Version 2.0 of the .NET Framework
introduces several new features in this area to improve the usability of the generated proxy classes. To
enable custom proxy code generation, Schema Importer Extensions have been introduced.
A Schema Importer Extension is a type that can be registered with the Web services infrastructure
through code or via config and will be called during the schema import process to allow an extension to
interpret the schema and inject code into the proxy. During schema import, each extension is called in
the configured order for each schema type encountered, and the extension can choose to inject code or
to simply ignore that type in the schema. This mechanism allows developers to build extensions that will
map schema constructs matched by name, namespace, and/or shape to custom classes or code.
This type of control is useful when the client of a Web service has custom types that are much richer than
those generated by wsdl.exe that the developer wants to be used by the proxy. Prior to the .NET
Framework 2.0, this was possible only by modifying the generated proxy. These changes were lost when
the proxy was regenerated. Schema Importer Extensions can now be developed and registered to map
schema type to the custom type every time the proxy is generated.
The following example shows a simple example of using a Schema Importer Extension to map a schema
type to an existing client class:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;
using System.Xml.Serialization;
using System.Xml.Schema;
using System.Xml;
using System.CodeDom;
using System.CodeDom.Compiler;
public class NetTunesSIE : SchemaImporterExtension
{
public override string ImportSchemaType(string name, string ns,
XmlSchemaObject context, XmlSchemas schemas,
XmlSchemaImporter importer, CodeNamespace codeNamespace,
StringCollection references, CodeGenerationOptions options,
ICodeGenerator codeGenerator)
{
if (name.Equals("songStream") &&
ns.Equals("http://demos.teched2004.com/webservices"))
{
codeNamespace.Imports.Add(
new CodeNamespaceImport("NetTunes"));
return "SongFile";
}
return null;
}
public override string ImportSchemaType(XmlSchemaType type,
XmlSchemaObject context, XmlSchemas schemas,
XmlSchemaImporter importer, CodeNamespace codeNamespace,
StringCollection references, CodeGenerationOptions options,
ICodeGenerator codeGenerator)
{
return null;
}
public override CodeExpression ImportDefaultValue(string value,
string type)
{
return new CodePrimitiveExpression(null);
}
}
To allow this extension to execute when generating proxy code, you need to register it in machine.config
using the following configuration section:
<system.xml.serialization>
<schemaImporterExtensions>
<add name="SongFile" type="NetTunes.NetTunesSIE, NetTunesSIE,
Version=1.0.0.0, Culture=neutral,
PublicKeyToken=abcdef0123456789" />
</schemaImporterExtensions>
</system.xml.serialization>
Once registered, this extension would cause the Schema type with a name of "songStream" in the
namespace of "http://demos.teched2004.com/webservices" to be mapped to the NetTunes.SongFile
type.
Web Services Anywhere
There are some interesting scenarios that require exposing a Web service from an application that is not
running in IIS; for example, running a Windows Forms desktop application that receives callbacks from
a remote server. Since version 1.0 of the .NET Framework, it has been possible to host ASP.NET
(including ASMX Web services) in any process, such as a console or Windows Forms application. Now
HttpListener makes it easier to do this. For example, the ASP.NET Cassini sample Web server is a
Windows application hosting ASP.NET.
Version 2.0 of the .NET Framework makes it easier to host ASP.NET and ASMX services in a managed
application using the new HttpListener class which sits on top of http.sys and handles listening for
incoming HTTP requests within your application. The figure below shows at a high level how a managed
application uses HttpListener to host ASMX services.
Performance
Pre-generation of serialization assemblies
To enable developers to modify proxy classes without having to worry about updating serialization code,
the XmlSerializer now dynamically generates serialization code for all proxy types the first time that the
proxy is instantiated. Despite the additional processing time required to generate the serialization code
and compile a temporary assembly on the fly, this approach works quite well in the majority of Web
service scenarios. Applications that have very complex proxies with numerous types may require a
longer period to generate the serialization code, and this has prompted the development of a new
deployment tool for pre-generating serialization assemblies. Version 2.0 of the .NET Framework
introduces sgen.exe, which can be run on the assemblies containing proxy classes to create a
serialization assembly that will be loaded at runtime to avoid dynamic generation and compilation.
This tool simply runs through all the types in an assembly and generates the necessary serialization code,
which can be loaded at runtime. Once you are ready to deploy a Web service client, you can use this tool
to generate a serialization assembly that can be shipped with your application's assemblies to
considerably reduce complex proxy instantiation times. Running this tool is as simple as:
C:\>sgen.exe MyProject.dll
This will generate MyProject.XmlSerializers.dll, which should be placed in the same directory with
MyProject.dll. At runtime, when the XML serializer finds this assembly, it will use it for serialization of
proxy types found in MyProject.dll.
SOAP Reply Compression
The .NET Framework HttpWebRequest class now supports decompression of HTTP replies. You can
take advantage of this feature combined with the IIS 6.0 compression feature to save on bandwidth and
potentially improve your application's response time. To enable decompression in the Beta 1 release of
the .NET Framework 2.0, you need to get the HttpWebRequest and set its EnableDecompression
property to "true" on the client side. This code snippet shows how to do that:
using System.Web.Services.Protocols;
using System.Net;
class ServiceProxy : SoapHttpClientProtocol
{
protected override System.Net.WebRequest GetWebRequest(Uri uri)
{
HttpWebRequest request=
base.GetWebRequest(uri) as HttpWebRequest;
if (request!= null)
{
request.EnableDecompression = true;
}
return request;
}
}
The final release of the .NET Framework 2.0 will have this property exposed directly on
SoapHttpClientProtocol so you will be able to write code like:
ServiceProxy proxy=new ServiceProxy();
proxy.EnableDecompression = true;
You will also need to configure IIS to enable compression of replies from Web services. See Knowledge
Base article 322603, HOW TO: Enable ASPX Compression in IIS for instructions on how to do this.
Commitment to Interoperability and Standards
Support for WS-I Basic Profile 1.0
The WS-I Basic Profile 1.0 specifies a subset of SOAP 1.1 and WSDL 1.1 designed to improve
interoperability between Web services. Following the guidance in Building Interoperable Web Services:
WS-I Basic Profile 1.0, you can build services that conform to the Basic Profile today using .NET
Framework 1.0 or 1.1. Version 2.0 of the .NET Framework makes it even easier to build Web services that
conform to the Basic Profile 1.0 by introducing a property named ConformanceClaims on
WebServiceBindingAttribute. Setting this property to WsiClaims.BP10 as shown below causes the
Web services stack to behave in a manner than conforms to the Basic Profile.
using System.Web.Services;
[WebServiceBinding(ConformanceClaims = WsiClaims.BP10)]
public class TheService
{ ... }
Note that there are some service features/settings that would break Basic Profile conformance and would
cause an exception when you invoke the Web service or request its WSDL. For example, if you use SOAP
encoding (by using SoapRpcService or SoapRpcMethod attributes), your service no longer conforms
to the Basic Profile. To use these non-conforming features, you simply need to indicate that your service
does not conform to the Basic Profile by removing the
WebServiceBindingAttribute.ConformanceClaims property.
When consuming any Web service, wsdl.exe will check the service's WSDL for Basic Profile conformance
and will display warnings if it finds the service to be non-conformant. These warnings let you know
upfront any conformance issues with the service's WSDL without preventing you from consuming the
service.
Support for W3C SOAP 1.2
Version 2.0 of the .NET Framework includes support for the W3C SOAP version 1.2 protocol in addition to
the existing SOAP version 1.1 protocol. On the server side, SOAP 1.2 is on by default and can be turned
off via configuration settings in machine.config or web.config:
<configuration>
<system.web>
<webServices>
<protocols>
<remove name="HttpSoap1.2"/>
</protocols>
</webServices>
</system.web>
</configuration>
With both SOAP 1.1 and SOAP 1.2 enabled on the server side, each Web service will support both
protocols, allowing the client to choose one of the two protocols. This increases the reach of your Web
services.
On the client side, you can select SOAP 1.1 or 1.2 by setting the SoapVersion property of the proxy class.
For example:
proxy.SoapVersion =
System.Web.Services.Protocols.SoapProtocolVersion.Soap12;
When using wsdl.exe to consume a Web service that supports both SOAP 1.1 and 1.2, you can specify
/protocol: SOAP12 on the command line to generate a proxy class that has its SoapVersion property
set to SoapProtocolVersion.Soap12. When you invoke the service you will be using SOAP 1.2.
In Conclusion
Web services and the XML serialization stack in the .NET Framework 2.0 offer a set of compelling features
that improve developer productivity, application performance, and standards conformance. We would
like you to install the Visual Studio 2005 Express Beta products (which include the .NET Framework 2.0),
build Web services, and send us your feedback using the dotnet.framework.webservices newsgroup