How come my generated client class doesn't inherit from the ServiceContract interface?

456 views Asked by At

I've recently started using WCF and was surprised to find that the client class that is generated by Add Service Reference doesn't implement the interface of the ServiceContract (let's call it IMyInterface). Instead it implements System.ServiceModel.ClientBase<IAnotherInterface>, IAnotherInterface where IAnInterface is an interface that is like my ServiceContract interface but not actually the same one. I see that some of the types are different, e.g. it has arrays instead of Lists.

This is a bit annoying for me as sometimes I want to make remote calls to a web service and sometimes want to use a local object, so I want them to implement the same interface. Currently I'm wrapping the ClientBase object in another one so it does implement the same interface, and in that just passing all calls through to the ClientBase object. Is this the recommended approach or is there an easier way?

I see I can change the types of the collection parameters from array to List or whatever on Configure Service Reference, but doubt that'll solve the basic typing issue.

3

There are 3 answers

0
marc_s On BEST ANSWER

The default behavior for WCF is exactly what you're seeing: the service and the client are totally independent of one another - after all with the idea of being interoperable, you cannot rely on the client being .NET, too - it could be Ruby, Python - you name it.

Therefore, when a client proxy gets created, the only thing that the client and the service share is the format of the messages on the wire, and in the case of SOAP the WSDL description of the service. The client-side proxy gets recreated based on this metadata, with the goal of providing the same methods to call, and the same data structures to use for serializing messages to the service.

If you can control both ends of the communication and both are .NET, then you can skip one of those steps by putting your service and data contracts into a separate class library, and sharing that assembly (and thus those exact interfaces and classes) between the service and the client.

When your client project references that shared assembly, and now goes to add a service reference and you leave the default options in place in the dialog, then the behavior is to re-use any existing classes and interfaces from referenced assemblies when possible. So in this case, if you add the service reference, the generator for the client-side proxy finds those classes that perfectly match your service description, and just uses those instead of generating a separate, independent set of classes and interfaces.

0
Reddog On

I think you want to move your service interfaces into a separate library (DLL/project).

Then there is an overload of SvcUtil.exe that allows you to generate the client proxies passing in a library (DLL) of interfaces so that they are not duplicated. I think the flag is -r or -reference or something like that...

0
Arron S On

You don't need to use the Add Service Reference or the SvcUtil. You can manually create a client proxy that uses your interface. You'll need a binding in your web.config, to use the default constructor, or you can pass in the endpoint and binding.

One big advantage of this method is that you don't have to fiddle around with SvcUtil.exe each time you change your interface and need to regenerate the proxy. You will get a compile error instead reminding you that this class isn't implementing your interface correctly.

SvcUtil and Add Service Reference is mainly used for remote services where you don't have access to the original service interface or contracts. The utility just reads the service wsdl and generates one for you. Hence the crazy naming to make sure it doesn't conflict with something already in your project.

Also, as Reddog mentioned, moving your Service and Data Contracts to another project/dll allows you to reference these easier.

public partial class MyServiceClient : System.ServiceModel.ClientBase<MyProject.Contracts.IMyService>, MyProject.Contracts.IMyService
  {

    #region IMyService Members

    public void RecordSearchAnalytics(int a, int b, int c, string d, string e)
    {
      base.Channel.RecordSearchAnalytics(a, b, c, d, e);
    }

    #endregion

    #region boiler plate code 
    public MyHServiceClient()
    {
    }

    public MyHServiceClient(string endpointConfigurationName) :
      base(endpointConfigurationName)
    {
    }

    public MyHServiceClient(string endpointConfigurationName, string remoteAddress) :
      base(endpointConfigurationName, remoteAddress)
    {
    }

    public MyHServiceClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :
      base(endpointConfigurationName, remoteAddress)
    {
    }

    public MyHServiceClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
      base(binding, remoteAddress)
    {
    }

    #endregion




  }