Mike Taulty's Blog
Bits and Bytes from Microsoft UK
Some new bits in ASMX Web Services in the “Whidbey” .NET frameworks
Mike Taulty's Blog

Mike's Badges

Follow on Twitter
View mike's profile on slideshare
Add to Technorati Favorites
CW Blog Awards

There have been a lot of public articles and presentations about what’s coming in the Whidbey version of the .NET frameworks and Visual Studio.NET 2005 for ASP.NET web forms. We have exciting features such as Personalisation, Master Pages, Code-Beside (did I call that the right thing?), HTML preservation, etc. etc.

 

What I’ve not seen as much of is what’s new for the person writing ASMX web services. So, we know that advanced web service scenarios are being targeted through the Web Service Enhancements (WSE) and that lets us reach the Web Service Architecture stuff (WS-*) in the medium term and in the longer term we’ll get Indigo but what can we do with Whidbey that we couldn’t do before with ASMX web services?

 

I thought I’d take a bit of a look round what I could find on ASMX in Whidbey – this is not meant to be “official” and nor is it meant to be “definitive”. I just wanted to take a tour around the MSDN bits and see what I could see. Mistakes and ommssions are mine – I’ll come back and fix them as I play more with these bits. Additionally, any code samples are provided “as is” with no implied warranty or rights and are not fit for any particular purpose – they’re just samples.

 

What I’ve documented here comes from the Whidbey Community Preview that was released at VSLive a month or two ago and is based on the bits that are available from MSDN subscriber downloads for people with MSDN subscriptions.

The Project System

 

The first change when you drop into the Community Preview and create a web services project is that the project type isn’t there anymore under the File->New->Project menu. It’s now under the File->New->Web Site menu. From here I can choose a language and the ASP.NET Web Service project type and I get a new project.

 

The nice thing that I spotted is that you can place this project anywhere you like – “c:\temp\testservice” was a good starting point for me rather than having to point it at a live web server. Indeed, one of the big changes for the Community Preview is that it comes with its own web server rather than requiring you to have IIS on the machine.

 

So, if you create a new web site (ASP.NET Web Service) then what you get is a folder that looks like this;

 

          TestService

                   Code

                             Service.asmx.cs

                   Data

                   Service.asmx

 

Just to check that my sanity was ok, I instantly checked that I can still make an ASMX web service in a single file and hacked the Service.asmx file to be;

 

So, with no codebehind and just a quick F5 on the keyboard I have my web service up and running just like VS.NET 2003 so I’m happy with that.

 

<%@ webservice language="C#" class="Test" %>

 

using System;

using System.Web.Services;

 

public class Test

{

    public Test() {}

   

    [WebMethod()]

    public string SayHello()

    {

        return("Hello");

    }  

}

 

So, what’s going on with this Code folder and the .cs files with it? Well, ASP.NET has a couple of new models for doing its dynamic compilation thing which leaves me with a few ways to partition my web service code;

 

1.      Put all the code into the ASMX file and leave it there. This will get compiled on an as-needs basis.

2.      Put no code in the ASMX file and have a codebehind file in the Code folder and put all the code in there.

3.      Put no code in the ASMX file and no codebehind and have the ASMX file reference a class I’ve built into a class library and deploy that in my bin or GAC.

 

In all cases, any “external” code is going to need to be found by the .NET assembly loader and so would need to be in a bin folder or the GAC.

 

I also get a new option for (1,2,3) above in that there’s a new WebSite->Publish menu option which will pre-compile the web site up for me leaving me with a set of bits that just need deploying to the web server. So, in this case there’s no visible code left in my ASMX file and everything’s been pre-built into the contents of a bin folder that VS.NET produces for me. It’s worth trying one of these and having a look at the outputs that you get to see what the difference is between this and any of (1,2,3) above.

 

Note that the project-less project system is just working off folders on the disk so I can just walk up to my Service.asmx file in a folder using Explorer and double-click on it to get back into VS.NET and see the file and the “project” (in terms of the folder structure) that it’s a part of. There’s no specific project or solution file being built up here for me.

 

The WebServiceBinding attribute and WsiClaims enumeration

 

However you choose to split up your code, one of the most obvious changes when you create a new project with the Whidbey Community Preview is that your web service class gets a shiny new attribute placed on it;

 

WebServiceBinding

 

Now, this attribute was in the framework V1.1 but it now has a new property named ConformanceClaims. This is an enumeration of type WsiClaims and is a way for your web service to state whether it claims to conform to the WS-I basic profile 1.0 or not.

 

If you state that your service is Base Profile 1.0 compliant then this will be checked for you at the point where the test page for your web service is generated. So, a good way of showing this is to change your web method that visual studio generates for you and add another attribute to it;

 

SoapRpcMethod

 

If you do this and then run the project you’ll see that the test page nicely flags for you that you no longer conform to the WS-I Basic Profile V1.0 and, in particular, you violate recommendation R2706 (cool!).

 

The WebServiceBinding attribute also has an EmitConformanceClaims flag on it which you can use to determine whether your WSDL should include the claims that you’re making. For instance, if you specify that you want conformance claims emitted then your WSDL operation will be decorated as below;

 

<wsdl:portType name="Service_asmxSoap">

  <wsdl:operation name="HelloWorld">

    <wsdl:documentation>

      <wsi:Claim conformsTo=http://ws- 

        i.org/profiles/basic/1.0 xmlns:wsi="http://ws-

        i.org/schemas/conformanceClaim/" />

    </wsdl:documentation>

    <wsdl:input message="tns:HelloWorldSoapIn"/>

    <wsdl:output message="tns:HelloWorldSoapOut"/>

  </wsdl:operation>

</wsdl:portType>

 

 

You will also notice that these two new properties appear on the WebMethod attribute to allow you control these same properties at the method level rather than the binding level.

Xml Serialization

 

The XmlSerializer is a key part of the ASMX infrastructure in that if you want to do the object<->XML mapping that’s required in going from CLR types to XML types and back again then XmlSerializer is your friend for doing this.

 

In V1.1 we had limited control over how types were serialized using the XmlSerializer. We could attribute types at design time and we could also override those attributes with new values at run time but it was all attribute based and the developer wasn’t really given full control as they were with (e.g.) remoting serialization.

 

It was reasonably well known that there was an interface called IXmlSerializable that certain .NET framework classes (the DataSet) used to do custom Xml serialization through the XmlSerializer but these interfaces weren’t documented for use.

 

In the Whidbey frameworks the IXmlSerializable interface is documented and supported for anyone to use if they need full control over how their types are serialized.

 

So, how does this work with ASMX web services? We’d hope that we can use types that implement IXmlSerializable as return types and parameter types to our “web methods” and we’re in luck because we can.

 

IXmlSerializable has 3 methods that you need to implement – ReadXml, WriteXml and GetSchema. I’m not 100% sure of the status of GetSchema right now as it doesn’t seem to be quite doing the right thing for me at the moment so I’m working a different way. Here’s an example of a simple class that handles its own serialization (in a very basic way);

 

[XmlSchemaProvider("ProducePersonSchema")]

public class Person : IXmlSerializable

{

       public Person()

       {

       }

       public Person(string firstName, string lastName)

       {

              _firstName = firstName;

              _lastName = lastName;

       }

       public static XmlQualifiedName ProducePersonSchema(

XmlSchemaSet set)

       {

              XmlSchema s = new XmlSchema();

              s.Id = "Test";

              s.TargetNamespace = "urn:types-mt-com";

 

              XmlSchemaComplexType t = new XmlSchemaComplexType();

              t.Name = "personType";

 

              XmlSchemaAttribute a = new XmlSchemaAttribute();

              a.Name = "firstName";

 

              t.Attributes.Add(a);

 

              XmlSchemaAttribute b = new XmlSchemaAttribute();

              b.Name = "lastName";

 

              t.Attributes.Add(b);

 

              XmlSchemaElement e = new XmlSchemaElement();

              e.Name = "person";

 

              XmlQualifiedName n = new

XmlQualifiedName("personType", "urn:types-mt-com");

 

              e.SchemaTypeName = n;

 

              s.Items.Add(t);

              s.Items.Add(e);

 

              set.Add(s);

 

              return(n);

       }

       public XmlSchema GetSchema()

       {

              return(null);

       }

       public void WriteXml(XmlWriter writer)

       {

              writer.WriteStartElement("person", "urn:mt-com");

              writer.WriteAttributeString("firstName", _firstName);

              writer.WriteAttributeString("lastName", _lastName);

              writer.WriteEndElement();

       }

       public void ReadXml(XmlReader reader)

       {

              // Not very robust this...

              XmlNodeType type = reader.MoveToContent();

 

              if ((type == XmlNodeType.Element) &&

    (reader.LocalName == "customer"))

              {

                     _firstName = reader["firstName"];

                     _lastName = reader["lastName"];

              }

       }

       public override string ToString()

       {

              return(string.Format("Person [{0} {1}]"));

       }

       private string _firstName;

       private string _lastName;

}

 

So, in our class Person we have implemented IXmlSerializable and we read/write Xml in the appropriate methods. Note that we have not implemented GetSchema but, instead, have attributed the class with an XmlSchemaProvider attribute which states that the schema for the serialized form of our class will be provided by the ProducePersonSchema method where we make up a schema each time and provide it back to the framework.

 

With that in place we should be able to use our type from a web service such as;

 

[WebService(Namespace="urn:mt-com")]

public class Service_asmx

{

 

      [WebMethod]

      public Person GetPerson()

      {

            return(new Person("Mike", "Taulty"));

      }

      [WebMethod]

      public void PutPerson(Person p)

      {

      }

}

}

 

Pre-Generating Serialization Assemblies

 

There’s some more stuff relating to XML serialization in that the way that the serializer works in the frameworks today is through compiling code at the point where we first come to serialize a particular type and this can be an “unexpected” hit to the performance of your system and has other implications around access to compilers. In Whidbey it’s possible to produce serialization assemblies a priori in order that you avoid this hit altogether (well, you take the hit at build time which is probably ok as you can do it whilst grabbing a coffee J).

 

There’s a new tool included with the SDK in the Whidbey preview named SGEN.EXE which performs this task for you. Now, I must admit that in the download I have when I run SGEN.EXE I get an exception saying that the strong name for SGEN.EXE can’t be verified. If this is the same for you and you’re playing then my way around it was to skip verification on SGEN.EXE by using the SN.EXE tool with a;

 

SN –Vr SGEN.EXE

 

which allowed me to work around this in order to experiment with it. So, in order to pre-generate a serialization assembly for your assembly containing serializable types you can do;

 

SGEN /assembly:myassembly.dll

 

And you can also specify /keep to keep the source code generated and one or two other options (see SGEN /?). If you do keep the source then you can have a poke around and see that you end up with classes derived from XmlSerializationReader and XmlSerializationWriter that are made bespoke for your particular class(es).

 

So, having got this assembly what do you do with it? How do you make sure that this thing is used when your types needs serializing rather than the framework cooking up new serialization readers and writers?

 

Well, there are certainly overloads of Serialize and Deserialize on the XmlSerializer that allow you to explicitly pass serialization readers and writers, but what if you don’t control the code that’s doing the serialization? How does that work? How do we get an implicit link between a type and a pre-generated Xml serialization assembly?

 

From what I can see (i.e. from a bit of testing) at the point where the XmlSerializer is constructed the framework goes out looking for a pre-built assembly that does the serialization (there is an assembly-wide attribute XmlSeralizerAssembly placed on the Xml serialization assembly) for the particular type being used. So, in my tests when I construct an XmlSerializer for my type my pre-generated assembly is loaded if it’s locatable in my bin path (or, presumably, GAC) and, if not, then a temporary assembly is compiled and loaded for me.

 

Generics in Serialization

 

For the Whidbey release, the CLR gets a huge addition in expressive power in that Generic types get added with full support in the runtime. Xml serialization has been extended in order to support generic types for serialization and de-serialization.

 

So, what can we do here? We can write generic types such as the one here;

 

[XmlRoot("Key")]

public class DictionaryKey<K, T>

{

      public DictionaryKey()

      {

      }

      public T Value

      {

            get { return(_value); }

            set { _value = value; }

      }

      public K Key

      {

            get { return(_key); }

            set { _key = value; }

      }

      private K _key;

      private T _value;

}

 

And we can use Xml serialization to serialize and de-serialize particular specialisations of this type by making a serializer for ourselves such as;

 

s = new XmlSerializer(typeof(DictionaryKey<int, string>));

 

So, we can write web services where we use parameters and return-values of generic types – whilst I’m pretty excited about generics in the run time I’m not (yet!) so excited about the use of generic types in these circumstances as I’m thinking that the focus is more on message-exchange as opposed to type-system representation. I might be missing a trick here though J

 

Customising WSDL and Proxies

 

There’s a new concept of  a Service Description Format Extension (SDFE) – we can use this to add elements to the WSDL generated for a service and also to extend proxies as they are generated.

 

In the Whidbey docs that come with the Community Preview there’s a good explanation of how to build one of these things so I won’t duplicate it here – look for the topic named “Customizing the Generation of Service Descriptions and Proxy Classes”.

 

From what I can see of this it’s pretty powerful (and can be used to build the /sqltypes stuff below).

 

WSDL.EXE and Proxies

 

The wsdl.exe tool is used for two primary purposes – generating proxy classes to allow us to talk to a web service that’s described by WSDL and generating server side classes which give us a head start in implementing a web service that matches a pre-written WSDL.

 

In the Community Preview we’ve got a few new flags and parameters added to the WSDL tool, let’s take a look.

 

WSDL.EXE /sqltypes

 

In the build that I’ve got of WSDL.EXE I couldn’t get the /sqltypes flag to do anything for me – possibly my error or possibly the tool. My understanding is that this is analogous to the “Customising WSDL and Proxies” section above.

 

WSDL.EXE /sharetypes

 

There’s a scenario today where you build a single type (say Customer) and you build 2 web services around it. Say on web service 1 you’ve got a method named “GetCustomer” and on web service 2 you’ve got one named “PutCustomer”. You create Customer as a common type in a library and you reference that common type from your other 2 web service projects. This is all fine until you get to the point where you create a single client project and you want to add web references to both service 1 and service 2 and you’re expecting to pass the Customer instance that you get back from GetCustomer() into a call to PutCustomer(). At this point you’ll find that the code that’s been built for you has created 2 different customer types in two different namespaces so there’s now some manual editing stuff to do to make sure that this works ok and that needs some care because the chances are that you’ll regenerate those proxies at some point and lose your manual changes.

 

I’m not quite sure how you drive this from the development environment just yet. What I have found is that if I’ve got 2 web services with a common type shared between them then I can do;

 

WSDL.EXE /sharetypes http://service1/service1.asmx?wsdl http://service2/service2.asmx?wsdl

 

And that seems to build me proxy code which will let me use that shared type across these two web services without any issue. I’ve a feeling that there’s a little bit more to this than I’ve written here so need to revisit.

 

WSDL.EXE /fields

 

The /fields parameter seems to do what it says on the tin in that it replaces public properties on your proxy classes with public fields instead. So, if we have a customer type with a firstname and lastname they will either be represented as a proxy class with properties or (with /fields) by fields. I haven’t checked this against 1.1 but, as I remember it, in 1.1 you got public fields whereas now you get public properties but you can change this using the /fields flag.

 

WSDL.EXE /protocol

 

We can use the protocol to switch how we talk to the web service. Choices are SOAP, SOAP12 (!), HttpGet, HttpPost.

 

Going with HttpGet gives you a proxy class derived from HttpGetClientProtocol with methods attributed with HttpMethod attributes that look to use classes such as UrlParameterWriter to write the “parameters” on to the Url presumably. Haven’t seen this stuff before – not 100% sure that it’s brand new but I’m guessing that it is.

 

Going with HttpPost gets you a proxy class derived from HttpPostClientProtocol and this looks to use classes such as HtmlFormParameterWriter to move your XML data up to the web site on a form.

 

Going with SOAP uses the familiar SoapHttpClientProtocol base class for your proxy and going with SOAP12 seems to do the same but sets the SoapVersion property on the base class to SoapProtocolVersion.Soap12.

 

Proxies

 

I had a bit of a look around the proxy generated with WSDL.EXE (for the SOAP protocol). Firstly, it seems to have a new asynchronous pattern implemented. For a “webmethod” named DoSomething we are used to seeing;

 

BeginDoSomething()

EndDoSomething()

DoSomething()

 

generated for us on the proxy side. In addition we now also see

 

DoSomethingAsync()

DoSomethingCompleted (event!)

 

On the proxy – the base class seems to now have BeginInvoke as previously and now also InvokeAsync – not sure how this is working or what the difference is here right now but it’s a difference. The proxy also has a CancelAsync method to stop one of these calls proceeding.

 

Proxy also gets a ConformanceClaims property and a SoapVersion property – don’t notice any other changes in there.

 

Configuration

 

I’m not sure if this is something that already happened in the V1.1 framework but I noticed that the default set of protocols that I was getting for my web services were SOAP and SOAP12. If you want to see Http Get and Post then you need to go into your configuration file and switch them back on.

 

Hosting Web Services

 

Today, if you want to host a web service you need a web server. So, web service work very well in a scenario where you have a client that makes requests to a web service, gets some data back and goes off to do something with it. Where it doesn’t work so well is where you want to build a publisher/subscriber model where a client registers interest in some data and the service later on notifies the client. This only works today if our notion of a “client” is defined to be somewhere where we can rely on the presence of a web server.

 

We can get around this today by using the Web Service Enhancements (2) which allow us to send bi-driectional SOAP messages without having a web server so we can have those call backs to the “client”.

 

But, can we achieve the same thing with ASMX? Both Windows Server 2003 and Windows XP Service Pack 2 have support for a kernel HTTP listener, HTTP.SYS and the frameworks already expose some essential pieces from the ASP.NET runtime that should allow us to put the two things together and host ASMX ourselves without a web server (well, without IIS anyway) inside of an application such as a service, a Windows Forms app or a Console app.

 

I need to come back to this as I have half a solution working right now – will repost when I have something working properly.


Posted Thu, May 20 2004 2:28 PM by mtaulty

Comments

mtaulty wrote re: Some new bits in ASMX Web Services in the “Whidbey” .NET frameworks
on Tue, May 25 2004 10:48 AM
Hey Christian - hope i didn't steal your thunder here but I'd been to so many "What's new in WebForms 2.0" and not seen anything about ASMX that I thought I'd sit down and have a look around. It was mostly haphazard wandering around the framework and tools plus a look at a slide-deck I found about Xml Serialization from Doug Purdy (from the PDC 03 I think).
mtaulty wrote re: Some new bits in ASMX Web Services in the “Whidbey” .NET frameworks
on Sat, Jul 3 2004 9:41 AM
The ConformanceClaims attribute gets flagged in VS 2005 Beta 1 with the following message:

'Public Property ConformanceClaims() As System.Web.Services.WsiClaims' is obsolete: 'This property will be removed from the product shortly'

It will be interesting to see if there's a replacement.

--rj
mtaulty wrote re: Some new bits in ASMX Web Services in the “Whidbey” .NET frameworks
on Thu, Aug 5 2004 10:32 PM
For SGen and even more (strongly-typed events from the XmlSerialization process) you don't have to wait until Whidbey:
http://weblogs.asp.net/cazzu/posts/SGen.aspx
mtaulty wrote re: Some new bits in ASMX Web Services in the “Whidbey” .NET frameworks
on Thu, Mar 10 2005 2:33 PM
I am using Visual Studio 2005 Beta 1. Unlike VS 2003 when I add a reference to a webservice to client web application it doesn't generate proxy file(.cs) but generates .wsdl, .disco and .discomap only. Why so ?
mtaulty wrote re: Some new bits in ASMX Web Services in the “Whidbey” .NET frameworks
on Fri, Mar 11 2005 1:22 AM
Hi,

I had this problem too. As far as I can imagine this must be a bug.

To solve the problem in the meantime, go to a .NET Framework command prompt and use WSDL.EXE with the URL of your service and it will emit a proxy that you can then add as an "existing item" to your project and you should be good to go.

Thanks,

Mike.
(C) Mike Taulty, 2009. All rights reserved. The information in this weblog is provided "AS IS" with no warranties, and confers no rights. This weblog does not represent the thoughts, intentions, plans or strategies of my employer. It is solely my opinion. Inappropriate comments will be deleted at the authors discretion. All code samples are provided "AS IS" without warranty of any kind, either express or implied, including but not limited to the implied warranties of merchantability and/or fitness for a particular purpose.
Powered by Community Server (Non-Commercial Edition), by Telligent Systems