So I spent a bit of time this evening mucking around with WCF Serialization and although it’s a little more complicated than I had originally suspected there are a number of big improvements that will immediately make my life easier. In this entry I’m just keeping notes on implementing a Web Service and client using WCF and making some observations…
So my scenario is the following: I have a set of Entity objects that I return as part of a service call (Web Service in this case) and these entity objects are complex objects that inherit from another object. My goal is to expose these objects via WCF through Web Service method calls.
With ASMX this process is straight forward: All I have to do is define my Web Service class mark it up with [WebService] and each of the individual [WebMethod] attributes for each of the exposed methods. Because the entity classes are marked as serializable and include a few strategic [XmlIgnore] attributes for several properties the process just works. ASMX creates the appropriate service wrappers and can take my objects directly as return values.
To implement the WCF service I added a WCF Service to my Web application which nicely creates a stub for me to fill in the blanks. WCF promotes a contract based model where you are supposed to define an interface or class that exposes the appropriate members for the service. This isn’t all that much different than with standard ASMX services at first blush: You use the [ServiceContract] attribute on the service and [OperationContract] attribute for each of the methods you want to expose.
By default the stub created creates an Interface definition, and a separate class. This isn’t required but recommended and the default for the generated stub class/interface. The contract is merely an interface with a few attributes defined:
[ServiceContract()]
public interface IWcfService
{
[OperationContract]
string HelloWorld(string Name);
#if Customer
[OperationContract]
CustomerEntity GetCustomer(string CustId);
#endif
[OperationContract]
MySubClass GetMySubClass();
}
Next is the Service class implementation which looks like this:
public class WcfService : IWcfService
{
public string HelloWorld(string Name)
{
return "Hello " + Name + ". Time is: " + DateTime.Now.ToString();
}
#if Customer
public CustomerEntity GetCustomer(string CustId)
{
CustId = "ALFKI";
busCustomer Customer = new busCustomer();
if (!Customer.Load(CustId))
throw new ApplicationException("Invalid Customer");
// Customer.Entity.LoadOrders();
Customer.Entity.Entered = DateTime.Now;
return Customer.Entity;
}
#endif
public MySubClass GetMySubClass()
{
return new MySubClass();
}
}
I decided to define my service in a separate project, rather than in the Web site, because I suspect I’ll be experimenting with exposing the service to different protocols later on. In order for that to work it’s a good idea to put the service into a separate project so you can host the service in different types of applications. So in the Web application I can create the Service through a WcfWebService.svc file which simply has a reference to these implementation class defined above:
<%@ ServiceHost Language="C#" Debug="true" Service="WcfService.WcfService" %>
This gives a service endpoint that can be accessed much in the same way as an ASMX Web service. However, this Web Service is now capable of servicing clients with more than basic HTTP – for example you can configure the service with a wsHttpBinding, basicHttpBinding and a couple of others that use compressed data on the wire. Right there is a big bonus over ASMX – you get a lot more flexibility over what goes over the wire. Even nicer: You can support multiple protocols through this single service endpoint and allow the client to decide which actual binding (the protocol) is used…
The bindings supported are determined through entries in web.config along with a host of additional GAC assemblies that must be loaded, all which comes courtesy of the ‘Add WCF Service’ options in Visual Studio. When you add a WCF Service your web.config will look something like this:
<configuration>
<system.serviceModel>
<services>
<service name="WcfService.WcfService" behaviorConfiguration="WcfServiceBehaviors">
<endpoint contract="WcfService.IWcfService" binding="basicHttpBinding" />
<!--<endpoint contract="WcfService.IWcfService" binding="wsHttpBinding" />-->
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="WcfServiceBehaviors">
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
<appSettings>
<add key="ApplicationConnectionString" value="Data Source=.;database=AjaxAspNet;integrated security=true;" />
</appSettings>
<connectionStrings />
<system.web>
<!--
Set compilation debug="true" to insert debugging
symbols into the compiled page. Because this
affects performance, set this value to true only
during development.
-->
<compilation debug="true">
<assemblies>
<add assembly="System.Security, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A" />
<add assembly="Microsoft.Transactions.Bridge, Version=3.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A" />
<add assembly="SMDiagnostics, Version=3.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
<add assembly="System.IdentityModel.Selectors, Version=3.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
<add assembly="System.DirectoryServices, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A" />
<add assembly="System.Web.RegularExpressions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A" />
<add assembly="System.Transactions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
<add assembly="System.Messaging, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A" />
<add assembly="System.ServiceProcess, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A" />
</assemblies>
</compilation>
<!--
The <authentication> section enables configuration
of the security authentication mode used by
ASP.NET to identify an incoming user.
-->
<authentication mode="Windows" />
<!--
The <customErrors> section enables configuration
of what to do if/when an unhandled error occurs
during the execution of a request. Specifically,
it enables developers to configure html error pages
to be displayed in place of a error stack trace.
<customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
<error statusCode="403" redirect="NoAccess.htm" />
<error statusCode="404" redirect="FileNotFound.htm" />
</customErrors>
-->
</system.web>
</configuration>
It looks like a ton of switches can all be set both in the Service attributes ([ServiceContract] [ServiceBehavior]) and then also be matched by settings in the config file both on the server and on the client. This is such a simple and logical thing, you gotta wonder why this functionality hasn’t been there all along <s>…
Note also that in the service definition code above the Interface and Class are implemented separately which is the default for what the WCF Service page sets up. Separate class and interface are recommended but it’s not required. You can define the [ServiceContract] and [OperationContract] attributes directly on the service class and that works just fine. The benefit of using an Interface is that you can gain some benefits in the future if you need to version your service (adding a new Interface to provide new functionality without breaking the existing contract), and if you want to use a single class to implement multiple contracts. Although it’s a little more work it’s probably a good idea to stick to the separation of ServiceContract and Implementation.
BTW, you can also separate the service contract in ASMX services in ASP.NET 2.0 by creating an interface with a [WebServiceBinding] attribute and marking interface methods with [WebMethod], then implementing the interface on a concrete class.
Serialization and Client Proxies
If you look at the service above you’ll notice that some of the methods return complex types and these types need to be serialized properly. Serialization in WCF works a bit differently than it does with ASMX and although it’s more complicated to understand it seems a lot more flexible once you get around the initial differences.
If you’re returning simple types from a Web Service nothing else needs to happen. It just works as you would expect with the values returned directly. However, if you return complex types you have the option of letting WCF do default serialization rules or providing WCF with an explicit DataContract layout of how you want to return your types.
By default WCF will do type marshalling and serialization for you, but default serialization works differently than it did in ASMX. ASMX services use XML Serialization which is property based and the WSDL and any Service proxies see marshaled types as a property mapped proxy of the original type published. WCF uses what is called DataContractFormat (similar to binary ISerializable semantics) however so it’s a Field based approach that deals with object state rather than the object interface (ie. Fields rather than Properties).
This has a number of ramifications. First it obviously works differently than ASMX so if you port a service that used types that were set up for XML Serialization and Xml based serialization attributes (like [XmlIgnore]) these attributes don’t have any effect. Instead you’ll have to use the equivalent ISerializable related attributes (like [NonSerialized]) and maybe more importantly apply them to Fields rather than properties. Things that worked fine for ASMX serialization may not work correctly for WCF serialization with the default [DataContractFormat] default.
However, WCF can still work with the ASMX style semantics by marking up the [ServiceContract] interface (or class) with [XmlSerializerFormat] and this might be handy if you’re migrating existing Web Services if they rely on XML serialization semantics.
I originally didn’t find this out and so I got bit by the different serialization rules in my business entities which allowed for Xml Serialization but had a number of private members that can’t be serialized. I had to manually go in and add [NonSerialized] attributes to a number of fields. No big deal (in fact probably a good thing since these types might be otherwise serialized as well), but something to remember. The good news is it’s not terribly difficult to adjust the code to make it work with [DataContractFormat] but it obviously depends on the application and how much and where the Xml semantics are applied.
DataContrractFormat serialization also has an effect on the WSDL and the service clients generated for proxies. If you have a plain object that is marked [Serializable] and you let WCF use it as a service message type, it will use the private field state to create this object. So my original:
Customer.CompanyName
Customer.Entered
On the server, turned into:
Customer._CompanyName
Customer._Entered
in the client generated proxy object. Basically WCF published the private interface which is maybe not quite expected. I had a discussion with Michelle Leroux Bustamonte and she had this great quote in response to one of my comments:
..as a matter of point you shouldn't care if the client proxy
generates properties or fields. Generating proxy implies metadata only, so
property accessors and/or methods have no meaning...be they public fields or
properties...it works the same. The code might look different to the client
(naming conventions inferred from schema) but the end result is they get an
object to serialize. That's the point.
She’s right of course! It’s one of those things that are easy to forget when you think of objects rather than messages as you typically should in Web Services. In the end these types aren’t really objects , but they are messages that are represented as objects. And WCF gets to decide what’s the best way to send them IF you don’t provide any guidance on how to format the message. So whether you pick up _CompanyName or CompanyName is a semantic side-effect that should have no bearing on the messaging.
Well, maybe… in the end I think we’ll have to be a lot more careful about just using stock types with WCF because of this difference. I think this is just another nudge that points us in the direction of explicitly defining a [DataContract] and laying out exactly what the message format’s going to be and starting to think more in terms of services and message rather objects and object oriented architecture…
To this effect WCF provides a more explicit message declaration mechanism which is to define a data data contract using the [DataContract] attribute on a class that is passed as a input or output message. By using a DataContract you can explicitly define a Message type and you get to determine how that type is serialized.
So the following class that serves as a message type solves the default DataContract serialization issue by explicitly defining the members that go into the serialized message and the resulting service description and client proxy:
[DataContract]
public class MySubClass
{
[DataMember]
public string SubName
{
get { return _SubName; }
set { _SubName = value; }
}
private string _SubName = "Rick Strahl Jr.";
[DataMember]
public DateTime Entered
{
get { return _Entered; }
set { _Entered = value; }
}
private DateTime _Entered = DateTime.Now;
}
The idea is that in a service based application you’re supposed to define your messages explicitly and this is one of the ways that you can do so. This implies however that you explicitly create your messages as a separate step rather than using existing types, or marking up existing types with these attributes.
The latter is very useful if you have existing container style objects in your application. For example, I use entity objects to hold business data – these objects have no functionality but are essentially data objects in the business layer. So these objects are actually a good fit to map to a [DataContract]. My entity objects get are auto-generated so with a small change to the generator and a switch to enable this feature I can have my business entities generated with the [DataContract] attribute for each entity class and the [DataMember] attribute for each property and so with that my entities marshal with the expected type definitions to the client.
Be aware though that if you add a [DataContract] to an inherited class, all classes in the class hierarchy must implement a [DataContract] even if no [DataMembers] are exposed. For example, my business entities inherit from a base entity class and the entities could not serialize until the base entity class was also marked with a [DataContract].
Note that this is not exactly optimal in terms of design however, because you’re now introducing part of the service layer into the business layer. At the very least the System.Runtime.Serialization (V 3.0) assembly must be added as a reference for this to work. SOA purists will probably tell you that using object types directly is a bad idea encouraging tight coupling, but I find that I have many scenarios where both client and server share the same exact business objects and this is a good way to get them to share on both ends of the wire.
Bypassing Proxy objects in Serialization
In direct relation to that particular scenario – client and server both sharing the same business objects -
I am back thinking about the whole issue of capturing Web Service result messages directly into ‘real’ objects rather than proxies generated message objects.
If I create a client side proxy in a WinForm app from my Web Service above, that proxy defines its own service Interface and Implementation classes, which are identical in signature but not in name by default.
So this works:
WcfServiceClient Client = new WcfWebService.WcfServiceClient();
WcfWebService.CustomerEntity Customer = Client.GetCustomer("ALFKI");
Where the CustomerEntity class here is the proxy generated one. But the following of course doesn’t work:
WcfServiceClient Client = new WcfServiceClient();
WCFBusObjects.CustomerEntity Customer = Client.GetCustomer("ALFKI");
because the type returned is actually not really a WCFBusObject.CustomerEntity even though both the client and the server share this same assembly and the server used this very type to serialize the object. So the proxy result type is incompatible with the actual business entity type.
In this scenario, to use the result entity in this scenario I have to take the returned result object, then parse all of the values out of it and stick back into a real instance of the Customer Entity:
NewCustomer.New();
Cust = NewCustomer.Entity;
Cust.CompanyName = Customer.CompanyName;
Cust.Address = Customer.Address;
Cust.PhoneNumbers.Fax = Customer.PhoneNumbers.Fax;
…
NewCustomer.Save()
But given that the type is shared by both the client and the server in an assembly that is referenced both by client and server wouldn’t it be nice if I could bypass deserialization into a proxy object and instead directly serializing into the entity?
I’m happy to report that WCF actually makes this possible if your objects implement a [DataContract] and then running SvcUtil.exe explicitly with custom flags. SvcUtil.exe is the utility that generates proxies from services and this tool provides a great deal of flexibility when creating these proxies.
One of the options of SvcUtil is to look at specified assemblies (/reference:) and look for matching [DataContract] classes in these assemblies. If matches are found these classes are then not generated with the proxy, which effectively lets you use the existing classes from the client application. In other words you can hotwire your own target types into the message deserialization sequence. Very cool!
To do this you have to SvcUtil manually (rather than the VS.NET ‘Generate Service Proxy’) with a command line like this:
svcutil /out:WcfServiceProxy.cs /d:..\..\WCFWinForm\WcfServiceProxy /r:..\..\BusObjects\bin\Debug\WcfBusObjects.dll
http://rasvista/wcfWebService/WcfService.svc
This says to generate a wcfServiceProxy.cs output file for the generated proxy, in the wcfServiceProxy directory, and include the wcfBusObjects.dll assembly as a reference looking the specified service (in this case an IIS hosted .SVC file).
The key is the /r: (or /reference:) switch and specifying any assemblies that might contain [DataContract] types. If a type that matches one of the service’s DataContract types is discovered no proxy message object is created. Only the Service proxy and any non-matched message classes are generated in the proxy source file. So now I can run code like this:
WcfServiceClient Client = new WcfServiceClient();
WCFBusObjects.CustomerEntity Customer = Client.GetCustomer("ALFKI");
NewCustomer.Entity = Customer;
If (NewCustomer.Validate())
NewCustomer.Save();
and WCF deserializes directly into my Entity objects. This is a relatively painless way to make this type mapping happen!
Separating assemblies
One thing became quite obvious as I was going through this WCF exercise and that is that it’s a good idea to separate out your Web application, your Service, your front end and your business objects into separate assemblies. For this simple test project I ended up with 4 projects:
- WcfWebService (Web Application)
- WcfService (Service implementation)
- WcfBusObjects (Business and Entity implementations)
- WcfWinForm (the test front end)
It seems pretty crucial to separate out the Service implementation if you plan to reuse the service. I would even suggest one more assembly for any Data Contracts so that the Data Contract can be shared between client and server applications where you are in control of both ends (unless these can actually live in the business layer as with the example above).
WCF – much more control, but a little more work
Overall – in the little time I’ve spent with WCF, I’m impressed with the capabilities. It seems whenever I ran into some snags or things that didn’t work outright they could be tuned with some sort of configuration setting and relatively easily so by setting a flag in a config file. The configurability is something that’s really quite nice even though it’s going to take a bit of time to get familiar with all the functionality exposed. There are a lot of knobs to turn, but it’s a surprisingly clean interface to do so – most of the settings are reasonably obvious and discoverability in the documentation and with Intellisense is good.
In informal testing it also feels that from the client side Web Service calls are considerably faster than with ASMX especially if you use a cached ChannelFactory directly. Also one of the biggest problems with the .NET 2.0 Web Service client has always been first time startup speed and that seems to be drastically better with the WCFclient – no more 5-10 second waits for the first time call to initialize...
I haven’t spent much time looking at other protocols at this point although this is next on my list of things to look into. This is certainly one of the biggest selling points of WCF and even with just the Web Service functionality and the ability to easily switch between Web Service protocols it’s clear that this is an immensely useful feature…
A big thumbs up on this stuff!!!
BTW, I've been going through a lot of different resources for my 'uh' research and here are a bunch of links that I found very helpful, since there isn't much yet in the way of books.
Michelle Leroux Bustamonte's WCF Book Preview (great because it covers lots of stuff!)
Juval Lowry - WCF Essentials - a developer's primer (excellent overview)
Aaron Skonnard - Serialization in WCF
Actually there are a several articles by all three of these guys and girls that are immensely helpful on MSDN. I look forward to the books though that will put all of this into a single place <s>...
Other Posts you might also like