Contents tagged with .NET 3.5
-
Case Switching on CLR Types
As most .NET developers know, you cannot do case/switch on CLR types and one of the reasons for it was explained pretty well years ago by Peter Hallam on the C# team.
But there are many cases where you would like to iterate through a list of objects if mixed types and do specific things depending on it’s type. For fun I started to try out different ways to do it, some are better than others but they all do the same thing, more or less. I’m also exploring method extensions, method chaining and lambdas and some of the samples almost started to become fluent and DLS like.
Note
Oh, I’m as far from a C# language expert as anyone can be, so there are other ways of doing this I’m sure. The random code below is just me playing around for a simple way of doing case/switching on types that worked in the code I’m currently working on.
Also, if you would like a derived class to do something special, you normally override a method, send in some parameters and let that code whatever it so special. That’s basic OOD, see the Eat() method in the sample code below. But there are cases where you for one reason or other would not like to do this. Enough of that, this is just for fun.
A List of Pets
I was working with a class hierarchy of pets like this:
namespace TypeCase
{
public abstract class Pet
{
public string Name { get; set; }
public abstract void Eat();
public override string ToString()
{
return Name;
}
public Pet Case<T>(Action<T> action) where T : Pet
{
if (this is T)
action.Invoke((T)this);
return this;
}
}
public class Dog : Pet
{
public override void Eat()
{
Console.WriteLine(Name + " eats cats.");
}
}
public class Cat : Pet
{
public override void Eat()
{
Console.WriteLine(Name + " eats mice.");
}
}
}
We got a Cat and a Dog which are both different types of Pet. They have a Name and they can Eat() which is good enough for testing.
Creating the List
I’m creating a simple typed List like this:
var pets = new List<Pet>
{
new Cat { Name = "Morris"},
new Dog { Name = "Buster"}
};
Now we have something to play with. First do something you often see, especially in .NET 1.x code.
Case Switching on Strings
It’s perfectly fine to switch on strings, so this is quite common:
foreach (var pet in pets)
{
switch (pet.GetType().ToString())
{
case "TypeCase.Cat":
Console.WriteLine("A cat called " + pet);
break;
case "TypeCase.Dog":
Console.WriteLine("A dog called " + pet);
break;
}
}
I’m not too fond of this, because you may rename Cat or Dog in the future, or change namespace of “TypeCase” to something else, and even though renaming stuff with Resharper is powerful, strings are often missed. It would have been nice to:
case typeof(Cat):
But that’s not allowed. The case must be a constant.
If Is
A much safer way is to use if … else if … and instead of using string comparing, check the type with the is statement. It’s also faster to type:
foreach (var pet in pets)
{
if (pet is Cat) Console.WriteLine("A cat called " + pet);
else if (pet is Dog) Console.WriteLine("A dog called " + pet);
}
This code is perfectly fine and I’ve used it many times. But what if I wanted to have a Case-like syntax?
Method Extension on Type
I’m thinking of a syntax like this one:
pet.GetType().
Case(typeof(Cat), () => Console.WriteLine("A cat called " + pet)).
Case(typeof(Dog), () => Console.WriteLine("A dog called " + pet));
In this case we’re extending the Type type with a Case method, like this:
public static class TypeExt
{
public static Type Case(this Type t, Type what, Action action)
{
if (t == what)
action.Invoke();
return t;
}
}
The Action parameter encapsulate the anonymous method we’re sending in, containing the stuff we want to do with the pet in question. In the Case() extension method we’re testing to see if we’re given the right Type and Invoke() the anonymous method if so.
Important Note: Without going into details, just make sure you don’t fall into a case of “Access to modified closure” when doing for(each) loops around anonymous methods. To be safe, you have to create a local pet-variable outside of the method:
foreach (var pet in pets)
{
//some code
var safePet = pet;
pet.GetType().
Case(typeof(Cat), () => Console.WriteLine("A cat called " + safePet)).
Case(typeof(Dog), () => Console.WriteLine("A dog called " + safePet));
//some more code
}
Better Method Extension on Type
But I’m not happy with this syntax. If feels more cumbersome than the if…is…if…else… syntax, and whenever you see the use of typeof() in code like this, generics can do some work for you. So I’m going for a syntax like this:
pet.GetType().
Case<Cat>(obj => Console.WriteLine("A cat called " + pet)).
Case<Dog>(obj => Console.WriteLine("A dog called " + pet));
This requires a new method extension:
public static class TypeExt
{
public static Type Case<T>(this Type t, Action<Type> action)
{
if (t == typeof(T))
action.Invoke(t);
return t;
}
}
Looks better, but you still risk getting into the issues with modified closure above and I would like to work on the “obj” parameter as if it was the Pet objekt itself, not the Type. Let’s make it even better:
Even Better Method Extension on Pet
Now I’m going for a syntax that looks like this:
foreach (var pet in pets)
{
pet.
Case<Cat>(c => Console.WriteLine("A cat called " + c)).
Case<Dog>(d => Console.WriteLine("A dog called " + d));
pet.Eat();
}
As you can see, the syntax is cleaner and I can work with the pet object itself as a parameter handled to anonymous method in the lambda statement.
To do this I have to create a method extension which knows about the Pet class:
public static class PetExt
{
public static Pet Case<T>(this Pet pet, Action<T> action) where T : Pet
{
if (pet is T)
action.Invoke((T)pet);
return pet;
}
}
It’s not a generic Case Switcher on Type, but it feels good to me and is easy to work with. And you don’t have the issue with access to modified closures with this one.
Refined Method Extension on List of Pets
I’m throwing in a final variant here, adding the Case method to the list itself:
pets.
Case((Cat c) =>
{
Console.WriteLine("A cat called " + c);
c.Eat();
}).
Case<Dog>(d =>
{
Console.WriteLine("A dog called " + d);
d.Eat();
});
pets.
Case((Cat c) => Console.WriteLine("A cat called " + c)).
Case<Dog>(d => Console.WriteLine("A dog called " + d));
As you can see, there are two ways syntactically to provide the type and the simple extension method for this variant looks like this:
public static class PetListExt
{
public static List<Pet> Case<T>(this List<Pet> pets, Action<T> action) where T : Pet
{
foreach (var pet in pets)
{
if (pet is T)
action.Invoke((T)pet);
}
return pets;
}
}
That’s it. I’ve seen a number of more complex ways to do roughly the same, but I’m not trying to create the ultimate Switch/Case framework, just playing around with c# to create something simple that may make the code easier to read (and fun to code).
-
Bad Request With WCF Service in Cassini On Windows 7 Beta
Trying to run a WCF service in Cassini on your Windows 7 Beta (7000) machine and get this error?
The remote server returned an unexpected response: (400) Bad Request.
Unless you’re running your service with basic http binding or with security specifically set to None in your bindings, you will get this security-related error. This problem was confirmed by Microsoft and will (hopefully) be fixed in next Windows 7 release.
The options you got:
- Create and run your own service host with code (I’ve been doing this in my integration tests until I tried Cassini and got the error)
- Use IIS (which I’m going to go for)
- Self-host it with WcfSvcHost.exe (which I’ve not tried yet)
Note that you need to add quite a few features to get WCF running in IIS running on Windows 7. See my previous post about this.
Richard Fennell had the same problems as well as a few others out there brave enough to try out a beta operating system.
-
Turn Windows 7 Features On or Off
I’m trying to install/add IIS features to my Windows 7 beta build 7000 laptop. You got pretty fine grained control over these features in Windows 7, and it’s not easy to know what you really need. I need to run WCF services on my machine, so I’m turning most things on, including WCF Activation stuff:
Then I’m adding these things that I think I need for your average ASP.NET application:
Finally I’m adding the management tools, including the IIS 6 compatibility stuff which I’ve been told is needed to play properly with VS2008:
Then restart and make sure you got everything in place.
-
Integration Testing WCF Services with Unity
I've been blogging a few times now about using Unity with WCF, but how do you integration test your service in an easy way without? The way I (and many others) do integration tests for a WCF service is by setting up my own service host and starting the service from test init:
[TestClass]
public class ServiceIntegrationTest
{
private static ServiceHost serviceHost;
[ClassInitialize]
public static void MyClassInitialize(TestContext testContext)
{
serviceHost = new ServiceHost(typeof(Service1), new [] { new Uri("http://127.0.0.1:8001/") });
serviceHost.AddServiceEndpoint(typeof(IService1), new BasicHttpBinding(), "Service1");
serviceHost.Open();
}
[ClassCleanup]
public static void MyClassCleanup()
{
serviceHost.Close();
}
[TestMethod]
public void Should_get_data()
{
var ep = new EndpointAddress("http://127.0.0.1:8001/Service1");
var proxy = ChannelFactory<IService1>.CreateChannel(new BasicHttpBinding(), ep);
var data = proxy.GetData(1);
Assert.IsTrue(data == "You entered: 1", "Got wrong data back, got - '" + data + "'");
}
}
By doing it this way I don't have to make sure Cassini is started up before the test or having to deploy the service to a local IIS. There is no need for web.config or app.config files and I don't have to add any service reference. This way of doing integration testing of services is described by many others, and it should work quite well on a TFS build server or similar.
Integration Testing WCF Service with Unity
A few blog posts away I wrote about using Unity with WCF, but how do you integration test a setup like that? Remeber that if you have created your own ServiceHostFactory, you specify the factory to use in the .svc markup using the Factory attribute this:
<%@ ServiceHost Language="C#" Debug="true" Factory="IRM.Patterns.UnityService.UnityServiceHostFactory" Service="WcfService3.Service1" CodeBehind="Service1.svc.cs" %>
The "problem" here is that the factory doesn't have any decent public methods to let you create the service host from a given service type. True, there is a CreateServiceHost method which accepts a string representation of the type, but that means your service host factory has to have a reference to the type in question. The way I went around that small issue is by creating a small "harness" around the factory, with a public method which accepts a Type:
public class UnityServiceHostFactoryHarness : UnityServiceHostFactory
{
public ServiceHost CreateServiceHost(Type serviceType, string baseAddress)
{
return CreateServiceHost(serviceType, new[]
{
new Uri(baseAddress)
});
}
}
A small change to the test-initialize method makes use of this test-harness:
[ClassInitialize]
public static void MyClassInitialize(TestContext testContext)
{
var serviceHostFactoryHarness = new UnityServiceHostFactoryHarness();
serviceHost = serviceHostFactoryHarness.CreateServiceHost(typeof(Service1), "http://127.0.0.1:8001/");
serviceHost.AddServiceEndpoint(typeof(IService1), new BasicHttpBinding(), "Service1");
serviceHost.Open();
}
Now we're running service integration tests and we have the Unity container loaded as well. I'm sure there are other, smarter and for me uknown ways of achieving the same, but it works for me :) If you want sample code for the UnityServiceHostFactory, please let me know, but know that my code is based on the this sample.
-
WCF Client Calling ASMX Service with Soap Headers
Need to send soap headers from WCF (Service Reference) clients to older ASMX services? The ASMX service not handling the header properly? It may have to do with namespaces being set in the soap header XML in a way differently from what the ASMX service is expecting.
So, how do you create and add the same type of headers, with the correct namespace, in a WCF client? For every outgoing call?
First, a simple ASMX service for your pleasure to play with:
[WebService(Namespace = "http://tempuri.org/")]
public class Service1 : WebService
{
public MyHeader myHeader;
[WebMethod]
[SoapHeader("myHeader")]
public string HelloWorld()
{
if (myHeader != null)
return "Got header: " + myHeader.MyFirstValue + " " + myHeader.MyOtherValue;
return "Got no header!!";
}
}
That's it on the server side. Over to the client side... Well, the header itself can be either a class which implements the MessageHeader class, but I prefer to use a normal class decorated with DataContract attribute. Notice the namespace property which matches the one on the service above:
[DataContract(Namespace = "http://tempuri.org/")]
public class MyHeader
{
[DataMember]
public string MyFirstValue { get; set; }
[DataMember]
public string MyOtherValue { get; set; }
}
In ASMX clients you normally create a soap extension to add the header to every outgoing call, but here the ASMX soap extension is replaced by a (Client)MessageInspector in WCF, which is added to a client endpoint via a behavior. I'm cramming the whole sample implementation into one class:
public class AddSoapHeaderBehavior : BehaviorExtensionElement, IClientMessageInspector, IEndpointBehavior
{
#region IClientMessageInspector Members
public void AfterReceiveReply(ref Message reply, object correlationState) { }
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
var myHeader = new MyHeader { MyFirstValue = "Yeeehaaaw!!", MyOtherValue = "Gaaah!" };
var messageHeader = new MessageHeader<MyHeader>() { Actor = "Anyone", Content = myHeader };
request.Headers.Add(messageHeader.GetUntypedHeader("MyHeader", "http://tempuri.org/"));
return null;
}
#endregion
#region IEndpointBehavior Members
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { }
public void ApplyClientBehavior(ServiceEndpoint serviceEndpoint, ClientRuntime behavior) { behavior.MessageInspectors.Add(this); }
public void ApplyDispatchBehavior(ServiceEndpoint serviceEndpoint, EndpointDispatcher endpointDispatcher) { }
public void Validate(ServiceEndpoint serviceEndpoint) { }
#endregion
#region BehaviorExtensionElement Members
protected override object CreateBehavior() { return new AddSoapHeaderBehavior(); }
public override Type BehaviorType { get { return GetType(); } }
#endregion
}
The endpointbehavior and behaviorextensionelement member implementations are just boilerplate stuff that should be hidden as default behavior by WCF if you ask me, but you need to type this out.
Finally, the behavior must be loaded in the config (or via code):
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="MyEndpointBehaviors">
<ClientSoapHeaderAdderBehavior />
</behavior>
</endpointBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="ClientSoapHeaderAdderBehavior"
type="MyBehavior.AddSoapHeaderBehavior, MyBehavior, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
<client>
<endpoint name="Service1Soap"
address="http://localhost:4221/Service1.asmx"
binding="basicHttpBinding"
behaviorConfiguration="MyEndpointBehaviors"
contract="ClientProxy.Service1Soap" />
</client>
</system.serviceModel>
</configuration>
Hope it helps someone.
-
LINQ to XML in VB.NET and Using the Right Language for the Job
I'm almost always using C# in my .NET projects, unless I'm doing Office automation where the VB-way of dealing with optional parameters helps out making the code a bit cleaner.
The last week we've been upgrading ASMX-clients to become WCF-clients for a number of old .NET 1.1 and 2.0 projects, and we ended up with a bunch of app.config files with loads and loads of WCF client endpoint sections, each of them pointing at their own binding configuration. To manually clean this up would take hours and hours of tedious work which would probably result in more than a few errors.
So I thought maybe I could do search/replace with a regexp-capable editor... or try out XML Literals in VB.NET. I wanted to remove old behaviors, extensions and bindings, then add my own behaviors and extensions and finally change some attributes on each client endpoint. Doing this with XML Literals and XDocument/XElement in VB.NET was quite straight forward and didn't result in too many lines of code:
Imports System Imports System.Xml.Linq Imports System.IO Imports System.Linq
Sub FixupConfig(ByVal infile As String, ByVal outfile As String) Dim defaultBindingConfigurationXml = <binding name="defaultBindingConfiguration" maxBufferSize="1065536" maxBufferPoolSize="524288" maxReceivedMessageSize="1065536"/> Dim behaviorsXml = <behaviors> <endpointBehaviors> <behavior name="FacadeSoapEndpointBehavior"> <SetMaxFaultSizeBehavior size="100000"/> <ClientExceptionHandlerBehavior/> </behavior> </endpointBehaviors> </behaviors> Dim extensionsXml = <extensions> <behaviorExtensions> <add name="SetMaxFaultSizeBehavior" type="SomeType.SetMaxFaultSizeBehavior, SomeAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/> <add name="ClientExceptionHandlerBehavior" type="SomeType.ClientExceptionHandler, SomeAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/> </behaviorExtensions> </extensions> Dim xdoc = XDocument.Load(infile) xdoc...<system.serviceModel>.<behaviors>.Remove() xdoc...<system.serviceModel>.<extensions>.Remove() xdoc...<system.serviceModel>.Single().AddFirst(extensionsXml) xdoc...<system.serviceModel>.Single().AddFirst(behaviorsXml) xdoc...<system.serviceModel>.<bindings>.<basicHttpBinding>.Descendants.Remove() xdoc...<system.serviceModel>.<bindings>.<basicHttpBinding>.Single().Add(defaultBindingConfigurationXml) Dim elems = xdoc...<client>.<endpoint> For Each element In elems element.@bindingConfiguration = "defaultBindingConfiguration" element.@behaviorConfiguration = "FacadeSoapEndpointBehavior" Next xdoc.Save(outfile) End Sub
I've heard people recommend to use VB.NET when dealing with XML and I agree - use the right language for the job. When doing this - remember to import System.Linq or you'll miss some vital extensions like the .Add() and .Single() methods ;)
-
ASP.NET MVC Preview 5 Released
Hey, I'm just helping to spread the word! A sampled a few links and quotes that has already been posted to blogosphere for your pleasure and knowledge. :)
Download it here -> http://www.codeplex.com/aspnet/Release/ProjectReleases.aspx?ReleaseId=16775
We didn’t originally plan to have another preview. However, we implemented a few significant chunks of functionality and were dying to get feedback so that we could incorporate it into the product before Beta. It helps that with five or so of these interim releases, we’ve become pretty efficient producing these releases.
We plan to have our next release be our official Beta, which means we’ll have a lot more test passes to produce and run before we release the next one.
Some preview 5 related blog posts:
Brad Wilson wrote about changes to partial rendering and view engine in preview 5.
Maarten Balliauw wrote about easier form validation with preview 5.
Nick Berardi has a list of news and changes and also a few issues he's found and submitted bug reports for:
Derik Whittaker had a few comments regarding sealed classes in preview 5:
It looks like that they removed the sealed keyword from many of the Attributes such as HandleErrorAttribute, AuthorizeAttribute and various other existing Attributes.
However, looks like many of the other attributes (some new, some not) such as AcceptVerbsAttribute, ModelBinderAttribute and NonActionAttribute are still marked as sealed. Guys, please unseal all your stuff. If you have a very, very, very valid reason then fine seal them. But if not, let developers loose and unseal them.
-
Auto Postback with Javascript in ASP.NET MVC
I've looked this up twice now so I'm posting it to my blog for future reference and as a quick-tip for others. Say you got a web page with a dropdown/select listbox and you want to reload/auto postback the page when the selection changes.
One way to do this without involving a Javascript or Ajax library like jQuery (which probably is a good idea anyway :) is to use the "htmlAttributes" parameter of the Html.DropDownList() helper method and add a "onchange" attribute through an anonymous type. Something like this:
Select a name: <%=Html.DropDownList("Name", ViewData.Model, new { onchange = "doSomeJavascript()" })%>
To submit a form, for example:
<% using (Html.Form<HomeController>(p => p.DropDown(), FormMethod.Post, new { id = "myform" })) {%> Select a name: <%=Html.DropDownList("Name", ViewData.Model, new { onchange = "document.getElementById('myform').submit()" })%> <% } %>
The minimal DropDown() method of the HomeController class to support this sample looks like this:
public object DropDown() { var list = new List<string> { "Adam", "Bob", "Charlie" }; return View(new SelectList(list, Request["Name"] ?? list.First())); }
As you can see, the htmlAttributes parameter is available on many of the Html-helper methods and I'm using ot to add a name attribute to the HTML form as well.
End Note About Basic Knowledge of Javascript and Html
Heh, ASP.NET MVC sure forces you to dust off that old, basic knowledge of HTML and Javascript that I think every web developer should have but the ASP.NET programming model has made us forgot... One could argue that it's niggy gritty stuff that we shouldn't have to worry about, but for me it feels good to know I'm in full control of the generated HTML and I'm not sending one byte more on the wire than what's needed. Yes, it's possible to have the same control with standard ASP.NET applications and controls, but I've seen experienced developers make mistakes around this more than once. ViewState anyone? :D
-
Returning Json from RESTful Interface with WCF
Someone commented on an earlier blog post I did on REST, POX/POJO and WCF and the comment read:
How about REST WCF bits from .NET 3.5 SP1? Is it possible now to let the user decide in which format he wants the response (xml or json) like MySpace API for example?
The convention is to use a file like extension at the end of the resource to specify data return type (.xml or .json)
UPDATE/EDIT: Turns out I was doing this the hard way as there is support for json serialization right from the ServiceContract which makes this extremely easy. Just make sure to specify the ResponseFormat to be json. In a previous "version" of this blog post, I used the JavaScriptSerializer class, which is... dumb :)
First go take a look at the sample that Kirk Evans had on his blog.
Note that it may be easier to create a RESTful interface with ASP.NET MVC if you're into that tech, but that's another blog post.
So, first I'm modifying the REST interface somewhat, adding support for /details.xml and /details.json URI:
[ServiceContract] public interface IService { [OperationContract] [WebGet(UriTemplate="customers/{id}/details.xml")] Customer GetCustomer(string id); [OperationContract] [WebGet(UriTemplate = "customers/{id}/details.json", ResponseFormat=WebMessageFormat.Json)] Customer GetJsonCustomer(string id); }
As you can see, on the GetJsonCustomer() method, I'm specifying the ResponseFormat to be json. That's it :)
A sample implementation for this interface looks like this:
public Customer GetCustomer(string id) { return new Customer { ID = id, Name = "Demo User" }; } public Customer GetJsonCustomer(string id) { return GetCustomer(id);
}
Using Fiddler to simulate client request and see what comes out of our RESTful service, we get this result from the /customers/123/details.xml request:
<Customer xmlns="http://schemas.datacontract.org/2004/07/RESTfulWCF" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><ID>1</ID><Name>Demo User</Name></Customer>
...and this from the /customers/123/details.json request:
{"ID":"123","Name":"Demo User"}
-
More on RESTful Service with WCF and POX/POCO
Kirk Eveans wrote a blog post about Creating RESTful Services Using WCF, which gives you a good understanding of how to get started with REST on WCF. In his sample, Kirk has 2 methods in a REST interface:
[ServiceContract] public interface IService { [OperationContract] [WebGet(UriTemplate = "customers/{id}")] Customer GetCustomer(string id); [OperationContract] [WebInvoke(UriTemplate = "customers")] Customer PostCustomer(Customer c); }
The Data Contract for Customer looks like this:
[DataContract(Namespace = "")] public class Customer { [DataMember] public string ID { get; set; } [DataMember] public string Name { get; set; } }
Kirk also describes how to use the Fiddler tool to send REST request to the service, which is a wonderful tool for these circumstances.
Now, to get the customer with ID 123, just send a GET request to the url: http://127.0.0.1:8000/customers/123 and the service will return:
<Customer> <ID>123</ID> <Name>Demo User</Name> </Customer>
To call the other method, PostCustomer(), send a POST request to http://127.0.0.1/customers with the following request body:
<Customer> <ID>123</ID> <Name>Demo User</Name> </Customer>
This returns:
<Customer xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <ID>123</ID> <Name>Hello, Demo User</Name> </Customer>
NOTE: Remember that you must add a HTTP header, specifying the content type, or the POST request will fail (Content-Type: application/xml).
Wrong Order of Nodes?
But what if the programmer sends the <Name> node before the <ID> node? Like this:
<Customer> <Name>Demo User</Name> <ID>123</ID> </Customer>
The service will then return:
<Customer xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <ID i:nil="true"/> <Name>Hello, Demo User</Name> </Customer>
Note that the ID is null! If the ID was declared as an integer in the Data Contract, the response from the service would be:
<Customer xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <ID>0</ID> <Name>Hello, Demo User</Name> </Customer>
Note that ID is 0 (zero), which could become a somewhat hard bug to catch. I asked Kirk about this and he confirmed the reason for this behaviour is the way the DataContractSerializer works. If no order is specified in the DataContract, it will (de)serialize in alphabetic order. If this is a problem for you, there is away around it by specifying the XmlSerializerFormat attribute on the REST interface:
[ServiceContract] public interface IService { [OperationContract] [WebGet(UriTemplate = "customers/{id}")] [XmlSerializerFormat] Customer GetCustomer(string id); [OperationContract] [WebInvoke(UriTemplate = "customers")] [XmlSerializerFormat] Customer PostCustomer(Customer c); }
POCO Support in SP1
One of the new features in .NET 3.5 SP1 is the support for POCOs - the DataContractSerializer supports serializing types that doesn't have the [DataContract] or [Serializable] attributes. Aaron Skonnard has a good post on this. This means you can safely get rid of the attributes on the Customer class:
public class Customer { public string ID { get; set; } public string Name { get; set; } }
Note that the DataContractSerializer is still picky about the XML it gets to be able to deserialize it properly. Again, to get a more "relaxed" REST interface where WCF accepts the Name and ID nodes in any order, use the XmlSerializerFormat. I'm not sure this is what you want, but it's an option.
I had a short mail conversation with Kirk about this, and he raised an interesting question about the lack of a industry accepted standard for describing RESTful services and I think he's right there. The XML-node order wouldn't be a problem at all if I gave the client programmer a schema or a contract which specified exactly how the RESTful interface was to be accessed and in which order the XML-nodes must come. Kirk had a lot to say about this, and I do hope he writes up a blog post about his thoughts ;)
There are ways to send and receive any XML to a RESTful interface with WCF, and I'll write a blog post about that another day.