Reputation: 63
I'm inexperienced when it comes to WCF and I can't really figure out a pain-free way to mock WCF services.
The situation: A client and a server that both have access to an interface defining the service, like such:
public interface ICustomerService
{
[OperationContract]
Customer GetCustomer(int id);
}
Now, my first question if there's a reason why you wouldn't wanna make the client and server share the same interface type that defines the service in a shared library. Of course, it simply isn't a possibility if the consumers of the service aren't .NET or if you expose it to third parties that don't have the library, but me sharing it when it is a possibility does not hurt those other scenarios, right?
Secondly, if it's not a bad idea, how do I actually get Visual Studio to reuse the service interface? I manage to get it to share the Customer
type by checking Re-use types
, that is also defined in the shared assembly, but it still re-generates the interface.
But regardless of those issues, though, how do I make the client mockable? If I go through the VS generated Service References, I get a concrete type to work with, but I don't want my code to ever refer to that type directly, I would like to talk to an interface. If I expose the generated client as ICustomerService
, which works, I don't have a Close
method, as the interface does not define it.
I also thought of the following approach and abandon the auto-generate client entirely and just write the client myself as it's trivial:
public interface IServiceClient<T>
{
void Close();
T Services { get; }
}
public class CustomerServiceClient : ClientBase<ICustomerService>, ICustomerService, IServiceClient<ICustomerService>
{
public Customer GetCustomer(int id)
{
return base.Channel.GetCustomer(id);
}
public ICustomerService Services
{
get { return this; }
}
}
That works and I can expose it as IServiceClient<ICustomerService>
to my IoC container, but with the caveat that it's now client.Services.GetCustomer(1)
and that I've lost the benefit of regenerating my client easily when the ICustomerService
interface changes. It's trivial code to add, but might still be annoying to maintain this.
Another possibility is taking advantage of the fact that the generated class is partial
. It also works when I do this:
interface ICloseable
{
void Close();
}
interface ClientInterface : ICustomerService, ICloseable
{
}
partial class CustomerServiceClient : IClientInterface
{
}
But that created a bogus class and interface, which isn't a disaster but not very pretty.
Before I go down either route, is there anything obvious I've overlooked?
Upvotes: 1
Views: 1535
Reputation: 32725
Have you tried Ninject.Extension.Wcf? There is an expample how to inject WCF services and it also shows how to close them.
https://github.com/ninject/ninject.extensions.wcf
http://teamcity.codebetter.com/project.html?projectId=project3&tab=projectOverview
Upvotes: 2
Reputation: 14272
If you are using Windsor as your IoC (not sure about Unity) you can get it to inject a WCF Client. That means your consuming code only has to worry about ICustomerService.
container = new WindsorContainer().AddFacility<WcfFacility>();
container.Register(Component
.For<ICustomerService>()
.ActAs(DefaultClientModel
.On(WcfEndpoint.FromConfiguration("CustomerService")))
.LifeStyle.Transient);
Upvotes: 0
Reputation: 164341
The auto-generate client is generally only useful for scenarios where you cannot share code between the client and the server. It will generate new types for you.
The correct way to share the contract (ie. the interface and the DataContract types it uses), is to define these types in a separate assembly, which should be distributed to both server and client. Then you can make the server implementation on the server and the client implementation on the client.
When you have the types readily available, it is easy to create your own client implementation, as you found out yourself. This is typically what I would do. This also makes it easy to mock and/or inject the service implementation.
If you dont have the option to share the contract assembly between client and server, the next-best thing would be to take advantage of the partial generated classes to add your interface and Close method.
Upvotes: 0
Reputation: 30195
No, sharing the interface in a single assembly does not hurt your prospects for non-managed clients. Don't worry about that.
As for another route to take - I've not worked with mocking frameworks, but I wonder if one of those might help. That said, have you considered promoting your testing needs from "unit tests" into "integration tests?" The way I've been testing my WCF services is to have a fake client using a real WCF client stub, which in turn uses the real server, and the real server is talking to a fake server controller. I then run both the client and server process - in a single process with appdomain magic. That single process runs in the context of Visual Studio's unit testing framework. So I'm running integration tests as if they were unit tests. Very nice!
Upvotes: 0