Right out of the gate, my biggest problem with WCF was understanding how to develop against a WCF service while still retaining the OO principles I've come to know and love. I really prefer to not have to change my development methodologies to fulfill the needs of whatever technology comes down the pike. For instance, I will never be fond of a technology that disallows me from using interfaces (as opposed to classes), and when I first started playing around with WCF this did indeed seem to be a problem.
If I create a WCF service method that deals with a person, for example, I might want to do this:
[ServiceContract]
public interface CustomerService
{
[OperationContract]
void DoSomethingWithPerson(IPerson person);
}You'll note that in the above code I use the "IPerson" interface instead of a class like in the below code:
[ServiceContract]
public interface PersonService
{
[OperationContract]
void DoSomethingWithPerson(Person person);
}I don't want to be forced to use a "Person" instead of an "IPerson". This is what I first struggled with getting done in WCF. I struggled with it because much of WCF is dependent upon serialization behavior, and WCF did not like me passing an IPerson instead of a Person. However, I managed to find hope...
Lets imagine we are dealing with IPerson and ICustomer interfaces, and we have a "Customer" class that implements ICustomer:
public interface IPerson
{
Name Name { get; }
}
public interface ICustomer : IPerson
{
}
public class Customer : ICustomer
{
private Name itsName;
public Customer(Name name) { this.itsName = name; }
public virtual Name Name
{
get { return this.itsName; }
}
}First of all, given the above code, how can WCF serialize the Customer class (let's forget about how we really want to use the IPerson interface with the service for a moment)? The answer is that it can't, since it isn't marked up with the DataContract and DataMember attributes, and it isn't serializable. What we can do is place the Serializable attribute on the Customer class:
[Serializable]
public class Customer : ICustomer
{
private Name itsName;
public Customer(Name name) { this.itsName = name; }
public virtual Name Name
{
get { return this.itsName; }
}
}We could also use the DataContract and DataMember attributes to allow WCF to serialize/deserialize the Customer class:
[DataContract]
public class Customer : ICustomer
{
[DataMember] private Name itsName;
public Customer(Name name) { this.itsName = name; }
public virtual Name Name
{
get { return this.itsName; }
}
}WCF will know how to serialize either of the above Customer classes. However, there is still a problem: because I want to pass an IPerson instance into my WCF service, and because the WCF serialization stream will contain no official type information in order to resolve what comes in from the stream to the type(s) that were originally serialized out to the stream, it has no way of knowing to resolve the IPerson instance it expects to an instance of Customer. This is where the ServiceKnownType attribute comes in handy on the service interface:
[ServiceContract]
[ServiceKnownType(typeof(Customer)]
public interface PersonService
{
[OperationContract]
void DoSomethingWithPerson(IPerson person);
}The ServiceKnownType attribute above tells the WCF deserializer which types to use when deserializing, and then the deserializer resolves the type name from the schema in the stream to a specific class, which in this case is the Customer class. You can apply multiple ServiceKnownType attributes to the service interface or to the methods in the interface. If you apply the attribute to the service interface, it essentially tells WCF to apply the type to all the parameters in every method in the interface; applying it to a specific method tells WCF to apply it to all the parameters in the method.
With this attribute applied to the service interface and with the Serializable attribute applied to the Customer class (and likwise for any other IPerson implementations), the serialization will not pose any problems. If you'd like an even more complete example to help step you through this, try this. This example adds a Salesperson class that inherits from IPerson and a number of other methods on the IPersonService interface (which the WCF service implements).
My friend Rob correctly pointed out that this is really not interface-based development at all, as one has to declare all the types that the service might expect beforehand, thereby breaking the flexibility that interface-based development brings to the table. He certainly is right, but this is at least one step on the road to building a service that can at least be semi-polymorphic, if there is such a thing. Really, the methods themselves that depend on the interfaces are polymorphic; the drawback is that one has to declare the potential types that are used in the service at compile time in order for WCF to conduct its serialization. This can cause headaches--perhaps migraines for some when creating tests for the WCF service. For example, this is not very pretty:
[ServiceContract]
[ServiceKnownType(typeof(Customer)]
[ServiceKnownType(typeof(CustomerMock)]
public interface PersonService
{
//Code...
}Am I supposed to place an attribute on my PersonService class for all the types of mocks I might feed into it?
I suppose there are two possible solutions to this that appear in my mind (and I must point out that at the time of writing this I am still a bit of a WCF novice):
Using ServiceKnownType and KnownType attributes certainly tightly couples the service to specific types, and I've been exploring how to take control of serialization to hopefully avoid this. My own complaint with this is that I don't want to have to take serialization over...I just want the framework to do it for me so I can concentrate on the problem at hand (the problem being the needs of the business).
And the better way of doing the above is located here.