Christian Weyer: Smells like service spirit

What's first?

October 2004 - Posts

The Web services empire strikes back - Support for Generics

Part 1: The Web services empire strikes back - Introductory thoughts
Part 2: The Web services empire strikes back - Inner Workings
Part 3: The Web services empire strikes back - Web Services in Visual Studio 2005
Part 4: The Web services empire strikes back - WS-I BP Conformance
Part 5: The Web services empire strikes back - Custom XML Serialization
Part 6: The Web services empire strikes back - Proxy Type Sharing
Part 7: The Web services empire strikes back - Contract-first with .NET 'IDL'
Part 8: The Web services empire strikes back - Schema Importer Extensions
Part 9: The Web services empire strikes back - Making asynchronous Web service calls easier
Part 10: The Web services empire strikes back - Support for Nullable and SqlTypes

To make the list of advanced data type support complete, we can build on the information just learnt about nullable types. XmlSerializer, and therefore also the ASMX v2 infrastructure, is now capable of serializing and deserializing generic data types that come to the developer’s palm with the .NET Framework 2.0.
The following generics-based declaration of a complex type to model a dictionary entry can be easily used in a Web service operation.

public class MyDictionaryEntry<K, V>
{
  private K _key;
  private V _value;

  public V Value
  {
    get { return (_value); }
    set { _value = value; }
  }

  public K Key
  {
    get { return (_key); }
    set { _key = value; }
  }
}

To use the above MyDictionaryEntry type as a return value for a WebMethod we can write the following method signature:

[WebMethod]
public MyDictionaryEntry<int, string> GetDictEntry()
{
  //
}

The ASMX infrastructure properly handles the generics type in our case emits the following schema snippet at runtime when querying the WSDL for the service (which may not be what you are really after ...)

<s:element name="GetDictEntryResponse">
  <s:complexType>
    <s:sequence>
      <s:element minOccurs="1" maxOccurs="1" name="DictEntry"
       nillable="true" type="tns:MyDictionaryEntry2OfInt32String" />
    </s:sequence>
  </s:complexType>
</s:element>
<s:complexType name="MyDictionaryEntry2OfInt32String">
  <s:sequence>
    <s:element minOccurs="0" maxOccurs="1" name="Value"
     type="s:string" />
    <s:element minOccurs="1" maxOccurs="1" name="Key" type="s:int" />
  </s:sequence>
</s:complexType>

The integer and string types we declared for our generics data type get completely mapped into the XSD space. So you can see that the power of closed generics types also comes to the Web services world.
Please note again, that this is not the way you should think of Web services: objects or .NET types. In the middle of the Web services world there is the message. You can model it with the code-first approach as propagated by ASMX and as used throughout this entire article. Or you could approach it from the contract-first corner – but this is a totally different story and leaves enough room for an entire new article.

Web services contract-first: Thoughts - Part 2

When you have your data (and a lot of data schemas are not just for Web services-related stuff), then you can start to model your messages. These messages will make up the operations in your Web service, depending on the message exchange patterns you need.

Model your messages

The Web services empire strikes back - Support for Nullable<T> and SqlTypes

Part 1: The Web services empire strikes back - Introductory thoughts
Part 2: The Web services empire strikes back - Inner Workings
Part 3: The Web services empire strikes back - Web Services in Visual Studio 2005
Part 4: The Web services empire strikes back - WS-I BP Conformance
Part 5: The Web services empire strikes back - Custom XML Serialization
Part 6: The Web services empire strikes back - Proxy Type Sharing
Part 7: The Web services empire strikes back - Contract-first with .NET 'IDL'
Part 8: The Web services empire strikes back - Schema Importer Extensions
Part 9: The Web services empire strikes back - Making asynchronous Web service calls easier

Not too many of our customers have complained in the past that there is no way to express null values for value types in .NET. Well, this is just the concept of value types, isn’t it? But it is an essential piece that is missing when working with XML schema-based types that allow null values. The .NET Framework 2.0 provides a solution for this.
Take a look at the following XML fragment that denotes a valid order:

<OrderStatus>
  <Id>42</Id>
  <OrderDate>2004-07-07</OrderDate>
  <ShipDate xsi:nil="true"/>
</OrderStatus>

Again, imagine our fits-it-all Web service to deliver information about order status. Based on the query criteria, the service may return the status of an order that has not been shipped yet. In this case the Web service returns a ShipDate element with the XSD instance attribute nil set to true.
The current version of the .NET Framework, or more correctly the proxy class generation and serialization process, already supports this. When you have an XSD or WSDL with a nillable or optional element, the proxy class contains an additional flag. In our example above this would be ShipDateSpecified. This flag indicates whether the value for this element is set or not. People need to be able to differentiate whether the element or attribute was missing from the XML document and whether the attribute was present but had the default value. The support for truly nullable types in the .NET Framework would make things a lot easier. Take a look at the following Web service code:

[WebServiceBinding(
 ConformanceClaims = WsiClaims.BP10,
 EmitConformanceClaims = true)]
[WebService(Namespace = "
http://www.thinktecture.com/demos/Nullable")]
public class NullableService
{
 [WebMethod]
 [return:XmlElement("MyData", IsNullable=true)]
 public SqlInt32 GetData(bool ReturnNull)
 {
    SqlInt32 mydata;
    mydata=(ReturnNull ? SqlInt32.Null : 1234);
   
  return mydata;
 }
}

This WebMethod returns a type of SqlInt32. After a quick look into the documentation we can see that it implements INullable. This is the essential interface that has to be supported when implementing your own nullable types. If we point wsdl.exe to the service’s interface and message contract it will generate the following code snippet:

[System.Web.Services.Protocols.SoapDocumentMethodAttribute(
"http://www.thinktecture.com
/demos/Nullable/GetData",
RequestNamespace="http://www.thinktecture.com
/demos/Nullable",
ResponseNamespace="http://www.thinktecture.com
/demos/Nullable",
Use=System.Web.Services.Description.SoapBindingUse.Literal,
ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
[return: System.Xml.Serialization.XmlElementAttribute("MyData", IsNullable=true)]
public System.Nullable<System.Int32> GetData(bool ReturnNull)
{
  object[] results = this.Invoke("GetData", new object[]
    {ReturnNull});
  return ((System.Nullable<System.Int32>)(results[0]));
}

As can be seen very clear, the proxy uses a generic type System.Nullable<System.Int32> to indicate that the actual instance of the return type may be null.
SqlTypes is also an example of a type that implements IXmlSerializable. The SQL types like SqlInt32 can then emit their own schemas and serialize/deserialize themselves. If we specify a special switch on proxy generation, the wsdl.exe tool can map nillable elements of primitive types (e.g. Int32, Date) to SqlTypes. So if you want to consume the above service, you might run wsdl.exe with the /sqltypes switch to get a proxy class that contains this code:

[return: System.Xml.Serialization.XmlElementAttribute("TheData",
IsNullable=true)]
public SqlInt32 GetData(bool ReturnNull)
{
  object[] results = this.Invoke("GetData", new object[]
    {ReturnNull});
  return ((System.Data.SqlTypes.SqlInt32)(results[0]));
}

Now we have a SqlInt32 data type present as return type of the method. This can come in handy when you are developing database-oriented and a bit more tightly coupled systems than it originally was intended by the Web services credo.

Web services contract-first: Thoughts - Part 1

Model your data based on schema (whether it be in raw angle brackets or beautified bricks, boxes and lines).

Model your data with XSD.

Look ma, it actually *is* a product!

I have been asked by a lot of people. And customers asked why to use WSE 2.0.
Well, besides the technical aspects, I told them: "Because it is a product. A product from Microsoft with *full* support, not just a technology sample.". And pointed them to this page.

The Web services empire strikes back - Making asynchronous Web service calls easier

Part 1: The Web services empire strikes back - Introductory thoughts
Part 2: The Web services empire strikes back - Inner Workings
Part 3: The Web services empire strikes back - Web Services in Visual Studio 2005
Part 4: The Web services empire strikes back - WS-I BP Conformance
Part 5: The Web services empire strikes back - Custom XML Serialization
Part 6: The Web services empire strikes back - Proxy Type Sharing
Part 7: The Web services empire strikes back - Contract-first with .NET 'IDL'
Part 8: The Web services empire strikes back - Schema Importer Extensions

Hands up!
Who thinks that the current programming model for asynchronously calling Web services (and not only that) is just a bit too clumsy? The ASP.NET Web services proxy generation framework currently creates proxies that offer asynchronous invocation via the async pattern used throughout the .Net Framework. While this pattern is powerful compared to older days, it is difficult for a lot of people to understand. And it is always quite a bunch of work and lines of code to implement. Honestly, some of us do not work directly with callbacks or delegates if we can have some higher level event structures. And as a logical consequence, it would be extremely helpful if we could invoke asynchronous Web service calls by simply making calls and then handling events to indicate completion. True love.

The so called RAD Async feature in ASMX v2 allows developers to use an event-based model for invoking Web services asynchronously which should result in much cleaner and much more stable code at the end of the day. This obviously needs some changes in the generated Web service proxy. For each Web service operation, there will be up to four things generated in the resulting service proxy implementation.

First, there is a class [OperationName]CompletedEventArgs which inherits from System.ComponentModel.AsyncCompletedEventArgs. This new class will provide the async result via a property called Result. The type of Result is the type of the synchronous method’s return value. So if you have a method in the Web service called GetData that returns a DataSet, you will have a corresponding proxy class called GetDataCompletedEventArgs with a property Result of type System.Data.DataSet. This generated class will also contain a property for each output parameter, if any.

The second thing that will be generated is a delegate named [OperationName]CompletedEventHandler which inherits from System.ComponentModel.AsyncCompletedEventHandler. This delegate has two parameters, the sender and an instance of [OperationName]CompletedEventArgs.
Next thing that will be generated is an event on the proxy class itself. The name of this event is [OperationName]Completed and its type will be the delegate [OperationName]CompletedEventHandler.

Finally, the asynchronous method itself is generated. There will actually be two overloads of this method. Both overloads will access all in and ref parameters. One of the overloads will also accept a parameter named asyncState of type object. This parameter will be used as a cookie to enable invoking an asynchronous method multiple times concurrently by the same client then distinguishing these calls when the [OperationName]Completed event is fired.

The best thing to do now is take a look at an example. Please consider the following WebMethod signature:

[WebMethod]
public DataTable GetProducts() { // … }

When we point wsdl.exe to the WSDL of the appropriate Web service we get code generated similar to the one below. Please note that all the attributes and comments have been omitted for brevity. I also just show the signature of the methods, no internal implementation.

public class DataService :
  System.Web.Services.Protocols.SoapHttpClientProtocol
{
  private System.Threading.WaitCallback GetProductsOperationCompleted;
  public event GetProductsCompletedEventHandler GetProductsCompleted;

  public System.Data.DataTable GetProducts() {…}
  public System.IAsyncResult BeginGetProducts(
    System.AsyncCallback callback, object asyncState) {…}
  public System.Data.DataTable EndGetProducts(
    System.IAsyncResult asyncResult) {…}
  public void GetProductsAsync() {…}
  public void GetProductsAsync(object userToken) {…}
  private void OnGetProductsOperationCompleted(object arg) {…}
  public new void CancelAsync(object userToken) {…}
}
public delegate void GetProductsCompletedEventHandler(object sender,
  GetProductsCompletedEventArgs args);

public class GetProductsCompletedEventArgs :
  System.ComponentModel.AsyncCompletedEventArgs
{
  private object[] results;
  internal GetProductsCompletedEventArgs(object[] results,
    System.Exception exception, bool cancelled, object userToken) :
      base(exception, cancelled, userToken) {…}
  public System.Data.DataTable Result {get {…}}
}

With this proxy file in place we can use the generated proxy code in a Windows Forms application like this (just a snapshot, not everything is shown):

private DataService ws;

public Form1()
{
  InitializeComponent();
  ws = new DataService();
  ws.GetProductsCompleted +=
    GetProductsCompletedEventHandler(ws_GetProductsCompleted);
}

private void button1_Click(object sender, EventArgs e)
{
  ws.GetProductsAsync();
}

void ws_GetProductsCompleted(object sender,
  GetProductsCompletedEventArgs args)
{
  dataGridView1.DataSource = args.Result;
}

Now, this is way easier than before. And if you still want to use the more hardcore and ‘real developer’-like approach and do it all on your own: go ahead, this is still supported.
BTW, the inner workings of the RAD Async feature is based on a class called BackgroundWorker. The pattern that the BackgroundWorker class defines is used throughout the .NET Framework 2.0. Not only the new Web service proxy classes use it but also the new asynchronous ADO.NET functionality.

Mono'ing: a small tool as a life-saver

Currently I am quite deep into understanding, installing, and configuring Linux (better said, SuSE Linux 9.1 Personal). Based on my now running and working configuration - which is hosted in a VPC image - I begin to dig into Mono.

Once you really understand how Linux ticks and how to install the myriads of interdependent modules (Red Carpet is a must-have!) - when you find *the* tool or lib on the net - it actually can bring a little bit of fun.

But there is always something that is not so good. I have a 22" CRT display which has been with me for the last four years and is from one of the best display manufacturers I know about. But ... there is always to few space on it. Even when running with the 1900 resolution. What can I do?
I need the VPC'ed Linux just from time to time because it should act as a server for my .NET applications running on Windows XP. I host some few Web services through
Apache (with mod_mono) on the Linux 'box' and connect to it - works great.
So: I want to have the Linux box visible but it should not waste any space on my main display ... there is help.

MaxiVista to the rescue. I just put the Linux VPC on my spare Tablet PC display which is configured as the secondary display for the MaxiVista primary display server app running on my main notebook (which is connected to the CRT).
Oh yes, besides the Linux VPC, my
iTunes and the Messenger window is also 'parked' there... now the Tablet PC has a reason to gather dust :)

I love it. Especially when you connect the machines with a fast LAN, USB or FireWire connection.

Posted: Oct 06 2004, 07:06 AM by CWeyer
Filed under:
Just curious: Linux programs

Tell me: which program available in any of the big Linux distros is so 'must have' that nearly everybody wishes to have it under Windows (... but it isn't there ...)?

Any feedback is appreciated. Thanks.

Posted: Oct 04 2004, 08:17 AM by CWeyer
Filed under:
More Posts