views:

189

answers:

3

Adding a service reference to a web service (this is all WCF) in Visual Studio produces some generated code including a client-side restatement of the interface being exposed.

I understand why this interface is generated: you might be consuming a 3rd party service and not have access to the actual interface.

But I do, and the two are not assignment compatible even though the transparent proxy does indeed exactly implement the interface to which I want to cast.

I can use reflection, but that's ugly. Is there some way to defeat this faux type safety and inject metadata to so I can use an interface with a class?


My specific problem departs from the norm in complicated ways that have to do with a single client that uses some derivatives of a base class directly and uses others remotely via service references. The base class for each server needs to keep references to subscribing clients in a collection for enumeration to notify events, and the problem was type varied due to the use of proxies.

None of these answers solves my specific problem, yet every single answer was instructive and helpful. I found my own solution (use a dual binding) but I would never have figured it out if you hadn't radically improved my understanding of the whole business.

Three excellent answers. How to choose just one? I choose the first, because it directly solves the problem I first thought I had.

+1  A: 

In order to return an interface from a service you need to use the KnownType attribute:

Does any of that help?

flesh
+2  A: 

When you add the service reference, go to "Advanced" and make sure "Reuse types in referenced assemblies" is selected and that the assembly containing your interface definition is selected. You can also do this with an existing service reference by right clicking on it and going to "Configure".

davogones
for a moment there I thought you had it, but this is already selected.
Peter Wone
+4  A: 

If you already have the contract dll at the client, you don't even need a service reference (unless you are using it to write the setup code for you) - you can simply subclass ClientBase and expose the Channel, and use that directly - something like (no IDE handy...):

public class WcfClient<T> : ClientBase<T> where T : class
{
    public new T Channel {get {return base.Channel;}}
}

Then you can just do things like:

using(var client = new WcfClient<IFoo>())
{
    client.Channel.Bar(); // defined by IFoo
}

You still need the configuration settings in the config to determine the address, binding, etc - but less messy than proxy generation. Also, you might choose to re-implement IDipsoable to deal with the fact that WCF proxies can throw in Dispose() (which is bad):

public class WcfClient<T> : ClientBase<T>, IDisposable where T : class
{
    public new T Channel {get {return base.Channel;}}
    void IDisposable.Dispose() {
        try {
           switch(State) {
              case CommunicationState.Open: Close(); break;
              // etc
           }
        } catch {} // swallow it down (perhaps log it first)
    }
}
Marc Gravell
That's very funky. I will use that at some point, I'm sure of it. If only to demonstrate my (borrowed) coolness.
Peter Wone
That's an interesting approach. I take this one step further in my projects and simply call the concrete class directly instead of using WCF. No point in sending SOAP over the wire. I still program to the service interface, but inject a concrete implementation at runtime using dependency injection.
davogones
@davogones - in my actual code, I use an interface `ILease<T>`, which is `IDisposable` and has a `T Service {get;}` - my `WcfClient<T>` implements this, as do a number of other implementations (direct DAL, POX/HTTP, etc). Very versatile. I use DI to provide the `ILease<T>` on the fly...
Marc Gravell
...so the code never knows about WCF - only ever `ILease<T>`; the "lease" is because I treat it as a time-bound (`IDisposable`) handle to a service.
Marc Gravell