WCF Service Reference generates its own contract interface, won't reuse mine
Asked Answered
M

4

41

My first question so hope it is suitable:

Shared interface assembly - I have a 'shared' assembly which has an interface, let's call it IDocRepository. It's marked with [ServiceContract] and there are several [OperationContract]-marked methods.

WCF implementation assemblies - I have two WCF service projects, each referencing the shared assembly, each implementing that interface as a WCF service.

Consumer assembly - Finally, I have a 'client' project, also referencing the shared assembly, with a reference to each of the two WCF services.

However, the service references generated in the consumer assembly derive from an auto-generated version of the interface:

public partial class ExampleClient : System.ServiceModel.ClientBase<SomeNamespace.ExampleSvcRef.IDocRepository>, SomeNamespace.ExampleSvcRef.IDocRepository {

What I expected
I would have hoped that both references would instead automatically inherit the interface I defined, that the consumer/client assembly is also referencing. Kind of like the re-use of classes that it provides for parameter and return types, but for the service interface.

Why
So that I can create an instance of either service reference proxy and cast it to my interface type.

So I could modify the generated code by hand each time, but there should be better way...?

(edit: I do have 'Reuse types in referenced assemblies' and 'Reuse types in all referenced assemblies' options selected for both service references)

Mapping answered 25/6, 2010 at 15:34 Comment(0)
T
47

"Reuse types in referenced assemblies" only allows you to reuse Data Contracts, not Service Contracts. If you want to share Service Contracts, you don't need to use "Add Service Reference" at all. You can just use ChannelFactory directly.

// Supply the binding and address in code
Binding binding = new BasicHttpBinding();
EndpointAddress address = new EndpointAddress("http://tempuri.org/address");
IServiceContract channel = ChannelFactory<IServiceContract>.CreateChannel(binding, address);

// Or read them from the config file
ChannelFactory<IServiceContract> channelFactory = new ChannelFactory<IServiceContract>();
IServiceContract channel = channelFactory.CreateChannel();

The channel object will also implement ICommunicationObject, so you can cast it if you need to call methods like Open() or Close().

Technocracy answered 25/6, 2010 at 22:54 Comment(4)
I was going with the second option, but I get the error that there must be an EndpointAddress specified. I have an endpoint set up in the config file, with the same contract (interface) specified, but it doesn't seem to be looking there? Can you help?Mapping
Nevermind; you need to pass the endpoint configuration name into the ChannelFactory<> constructor.Mapping
Awesome solution thanks! I was able to eliminate the unwieldy huge service references completely in our project!Amaranthaceous
A few of the many bonuses of doing it this way is that you actually see the interfaces xml documentation (otherwise you don't because the ServiceReference doesn't copy the documentation) and you can use Resharper's Go to Implementation and Find References if your Service project is in the same solution.Amaranthaceous
D
4

Visual Studio does not support reusing you existing interface when generating the proxy classes for you. Reuse types will not reuse the contract interface as Quartermeister pointed out.

We have solved it with inheritance. Quite similar to the partial class idea above suggested by Jester Software.

This is how we solved it:

In the project of your client just create a service reference as you would have done. Then add a class that serves as the replacement for the client:

internal class MyServiceProxy : MyServiceClient, MyLogicNamespace.IMyService
{}

This class inherits from the generated MyServiceClient but states that that client does implement the original interface.

(I suggest you put them in a folder named "ServiceProxies")

If the MyServiceClient class contains any methods that do not match with the original interface then you can add them in that proxy and do the conversion in code.

After this, just use the MyServiceProxy where you would have used MyServiceClient.

Dorie answered 26/6, 2013 at 11:29 Comment(0)
X
3

When you create the service reference, there is a box you can tick to make it reuse the shared definitions. Make sure the client project is already referencing the shared assembly, add the service reference again, and check all the options carefully.

If it still doesn't work, check the binding you use. I have a vague recollection that basic HTTP binding won't support re-using of types?

Xylophagous answered 25/6, 2010 at 15:37 Comment(4)
Hi David, thanks for the comment! - I have done exactly that, I should have mentioned. :) 'Reuse types in referenced assemblies' is checked, and I have tried it with 'Reuse types in all referenced assemblies', and 'Reuse types in specified referenced assemblies' while manually picking the assembly in question - both with the same result (as above).Mapping
Only problem with that route is if you want to reference 2 services with the same shared lib; you get duplicated objects.Insouciant
@eschneider - I don't see how that's the case, that's what the 'Reuse...' option does, it stops the duplication of classes ('objects') by using the existing referenced classes - doesn't it? However I am seeing duplicated interfaces, which is the problem I have described in my question :).Mapping
I'm saying that duplication can happen (a warning); not that it's the problem in your case.Insouciant
E
2

There is another good option, if you want to continue to use the proxy generator for it's limited-but-somewhat-useful functionality... Use a partial class:

namespace <same namespace as generated proxy>
{
    public partial class MyClient : <namespace of "real" service contract>.IServiceContract
    {
    }
}

Ensure that the proxy is generating code the same way your Service Contract is defining it, ie, if it's using 'List', use that option in Configure Service References as well. In other words, make sure your generated Service Interface is exactly equal to your real Service Interface and the above code should work, and to update the reference you use right-click instead of writing code.

Ensue answered 2/9, 2011 at 16:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.