views:

1599

answers:

4

I have a WCF service hosted in IIS. The intention is for clients to make a call and receive a custom class that is defined in another project/dll. I have generated a service client using the svcutil.exe. The problem is this autogenerated client contains a new partial / proxy definition for the class I am trying to return from the service. It now throws a conversion error between my original custom class and the new partial definition at compile time. So how do you return user defined types from a WCF service? Advice appreciated.

+3  A: 

One of the things that will need to happen is that the user must configure the service reference to use your types from the DLL rather than the class defined by the proxy - http://msdn.microsoft.com/en-us/library/bb628653.aspx

Rich Reuter
+2  A: 

We have walked down that path in the past, and indeed the problem is that, of course, these are two different classes, so you will have to follow the link provided by @Rich Reuter;

We have, though, learnt the hard way why this is bad practice as it goes agains the 3rd tenet of SOA- "Services share schema and contract, not class".

Of course the problem is not just not following a "rule" set by someone at some point, but that there were great reasons for this to be suggested - we have learnt that the price of such tight coupling between the service and the client means it is very hard to release either - if the service needs to add another field to that class, to service another client - the first client might be affected; if the service than needs to change something in that class definition (to service another client) - the first client will again be affected, and vice versa - the client may affect the life cycle of the service.

In large projects this quickly becomes a huge maintenance burden.

Yossi Dahan
+3  A: 

Just to second Yossi's/Rich's thoughts:

  • yes, you can add a reference to the shared dll (rather than using the generated proxy class)
  • yes, it defeats a lot of the intent of data-contracts, and if any kind of custom serialization is happening, you may have issues extending your service

I have gone down this road before, and in some ways wish I hadn't. Re extensibility / custom serialization - you have to be very careful. A bit easier if you use a pre-rolled serializer such as protobuf-net (which can integrate directly into WCF, and which is designed with extensibility in mind), but not easy.

Actually, one advantage of sharing the classes is that it makes it a bit easier to test: since you have the same IFoo everywhere, you can mock that IFoo with reasonable chance of success. It is harder to mock when the proxy gets involved (as you change more moving parts between the test code and the production code).

Marc Gravell
Just to add: you can include existing dlls when calling svcutil using the /r switch - or via the VS IDE.
Marc Gravell
+2  A: 

If the type that you want to return from your service call is not marked as a DataContract then you will not be able to return it from WCF without providing a copy of that same assembly to your client application.

using System;
using System.ServiceModel;

[ServiceContract]
interface IService
{
    [OperationContract]
    TimeSpan GetTimeSpan();
}

class Service : IService
{
    public TimeSpan GetTimeSpan() { return DateTime.Now.TimeOfDay; }
}

Why does the previous code work then? It works because both sides of the service call have System.dll so they both know about the System.TimeSpan type which is the return type of the OperationContract GetTimeSpan().

Here is an example using a DataContract:

using System;
using System.ServiceModel;
using System.Runtime.Serialization;

[ServiceContract]
interface IService
{
    [OperationContract]
    Contract GetContract();
}

[DataContract]
class Contract
{
    [DataMember]
    public String MyProperty { get; set; }
}

class Service : IService
{
    public Contract GetContract() { return new Contract(); }
}

Now you have provided serialization attributes to a class you have defined (Contract) - this will allow you to use svcutil.exe to create proxy classes in your client application that will be serialized and sent to the WCF service.

Now if you want to return a type that is not a DataContract you must provide a copy of the assembly containing that type to your client application.

Andrew Hare
Your examples with string add a little confusion; string works because string is a type well known to the metadata layer; it doesn't really have much to do with System.dll, and isn't quite the same reasoning that applies to bespoke classes.
Marc Gravell
But how is that type well known? It is well known because both the client and service have the assembly. If that wasn't the case then it would be impossible to return System.String from a service call.
Andrew Hare
No, it wouldn't. String is a known type in both wsdl (soap) and mex (wcf); you can talk strings without going near .NET, assemblies, etc.
Marc Gravell
Ah - I see what you mean. Thanks for the correction!
Andrew Hare
I changed my example to use TimeSpan to better show what I mean.
Andrew Hare