I am writing a WCF client/service. The service can perform some long operations so I have added a callback contract IProgressCallback. The system I am developing has to run in all kind of environments so I suspect that I will run into an environment where a callback channel cannot be opened (I might be wrong here).
So to be careful I have defined the operation contract like this.
[ServiceContract()]
interface IContract
{ ... }
[ServiceContract(CallbackContract = typeof(IProgress))]
interface IDuplexContract : IContract
{ ... }
This works great on the server side. I can easily configure the service to use either contract.
The problem however arises on the client side. I have manually defined 2 proxies
public class ContractProxy
: ClientBase<IContract>, IContract
{ ... }
public class DuplexContractProxy
: DuplexClientBase<IDuplexContract>, IDuplexContract
{ ... }
Again both proxies work fine.
Now I have a proxy factory which is responsible for creating the correct proxy. It can easily figure out which proxy to instantiate but my problem arises when I try to return the proxy.
The user needs to get an object back that is at least IContract and ICommunicationObject but I haven't been able to find what to return. I have tried to the following:
IContract CreateProxy(...) // The user lacks access to Open, Abort, Close, etc.
ClientBase<IContract> CreateProxy(...) // DuplexClientBase derives from ClientBase but the generic parameter is different and it isn't covariant so this cannot be done.
// First define a generic interface and then make both proxies implement it.
IProxy<TContract> : TContract, ICommunicationObject // TContract cannot be restricted to an interface so I cannot make IProxy derive from TContract
Currently as a workaround I am simply returning the IContract interface but then everyone using the proxy will have to start by casting it to a ICommunicationObject to open the proxy etc.
Does anyone here have a better way to do this or am I simply being overly worried that duplex communication might not work in some environments?
Related
TL;DR:
What is a good and testable way to implement the dependency between the ViewModels and the WCF services in a MVVM client?
Please read the rest of the question for more details about the problems I encountered while trying to do this:
I am working on a silverlight client that connects to a wcf service, and I want to write unit tests for the client.
So I'm looking for a good solution for using the wcf clients in my ViewModels and testing that interaction. I have found two solutions until now:
Solution 1: This is actually how I have implemented it until now:
public class ViewModelExample
{
public ViewModelExample(IServiceClient client)
{
client.DoWorkCompleted += ..
client.DoWorkAsync();
}
}
//This is how the interface looks like
public interface IServiceClient
{
event EventHandler<AsyncCompletedEventArgs> DoWorkCompleted;
void DoWorkAsync();
}
//I was able to put the interface on the generated clients because they are partial classes, like this:
public partial class GeneratedServiceClient : IServiceClient
{
}
The good part: it's relatively easy to mock
The bad part: my service client lives as long as my ViewModel, and when I have concurrent requests I don't know which answer belongs to which request.
Solution 2: Inspired by this answer
WCF Service Client Lifetime.
public class ViewModelExample
{
public ViewModelExample(IServiceFactory factory)
{
var client = factory.CreateClient();
client.DoWorkCompleted += ...
client.DoWorkAsync();
}
}
The good part: each request is on a different client, so no more problems with matching requests with answers.
The bad part: it's more difficult to test. I would have to write mocks for both the factory and the wcf client every time. This is not something I would like to do, since I alreay have 200 tests... :(
So my question is, how do you guys do it? How do your ViewModels talk to the wcf services, where do you inject the dependency, and how do you test that interaction?
I feel that I'm missing something..
Try having a Func<IServiceClient> injected into your VM instead of the a client instance; you'll have a 'language-level factory' injected instead of building a class for this. In the factory method you can instantiate your client however you want (each access could create a new instance for that for example).
The downside is that you'll still have to touch your tests for the most part, but I assume it will be less work:
public ViewModelExample(Func<IServiceClient> factoryMethod)
{
var client = factoryMethod();
client.DoWorkCompleted += ...
client.DoWorkAsync();
}
The WCF service should have it's own tests that confirm the functionality of itself.
You should then be mocking this WCF service and writing unit tests within your consumers.
Unfortunately, it's a pain and something we all have to do. Be pragmatic and get it done, it will save you getting bitten in the future.
Are you using IoC container by a chance? If you had, this problem would be totally mitigated by container (you'll simply register IService dependency to be created as brand new upon each request).
If that's not the case, then
I would have to write mocks for both the factory and the wcf client every time
is how you deal with this kind of "problems". The cost is relatively small, probably 2-3 extra lines of code per test (all you have to do is setup factory mock to return service mock, which you do need either way).
I'm in the process of writing a duplex WCF service using NetTcpBinding, and I've run into an architecture question that I think I know the answer to, but hope that I'm wrong.
Our service is stateful, and we've selected NetTcpBinding with PerSession InstanceContextMode. For various reasons, this is something that we require. I'm trying to break up our larger interface (where large blocks of the operations would not apply to many clients) into multiple smaller interfaces with the operations logically grouped. While it's simple enough to have a single service implementation implement all of the contracts, I'm not sure if it's possible to have multiple service contracts share a single channel (or, more to my requirement, a single session), and I'd definitely need to be able to do that in order to make this work.
I could, of course, include everything on one contract and throw FaultExceptions when an invalid operation is performed, but I'd really like to be able to break these up and not even add an endpoint for inapplicable contracts. Is what I'm looking for possible?
TL;DR Version:
I need to be able to do this:
[ServiceContract]
public interface IServiceA
{
[OperationContract]
void Foo();
}
[ServiceContract]
public interface IServiceB
{
[OperationContract]
void Bar();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class Service : IServiceA, IServiceB
{
...
}
And be able to establish one session from the client to the service but use both IServiceA and IServiceB.
The default instance provider over a sessionful channel will give you an instance per connection in your case. You can however extend the instance provider to pick up an existing object from your own cache and return the same object.
How you correlate instances will be upto you using some special message header etc. The underlying channel/Connection will be different for each proxy and also use differnt buffers / concurrency models but you can allow service model to use the same instance.
http://msdn.microsoft.com/en-us/magazine/cc163590.aspx
I'm making a WCF service with netTcpBinding which has a main lobby with multiple chatrooms which the clients can enter. The Lobby class implements ILobby as the service contract.
When a client wishes to enter a room I want to callback the client exposing a new Channel containing the InstanceContext for the room he just entered but after much searching I am doubting that this is possible.
For example on the Service side I might have
class Lobby : ILobby
{
Dictionary<string, Chatroom> rooms;
public void JoinRoom(string roomname)
{
if (rooms[roomname].TryEnter()) {}
}
}
class ChatRoom : IChatRoom
{
public bool TryEnter(string username)
{
ILobbyCallback callback =
OperationContext.Current.GetCallbackChannel<ILobbyCallback>();
// How do I do this next bit?
callback.JoinedRoom(pass some instance context here);
return true;
}
}
On the client side callback method I want
public void JoinedRoom(InstanceContext for the room on the service side)
{
// Create a new WCF proxy using above InstanceContext
// Create a WPF UI for the new room passing the proxy so it can communicate
// with the room class directly without going via the root service
}
Is this possible? What's the best practice for spawning new classes with their own contracts on the service side? Or do I just have to bundle everything into one massive MyService class and handle everything myself?
You cannot pass instance context as parameter to any operation contract. It doesn't make sense because that context has local scope. It is called "instance context" = it is context of current service instance. In duplex scenario both client and server has its own service:
Clients calls server's service through its proxy
Server calls client' service through received callback channel
Server's service instance context has meaning only on the server. It is not clear what you are trying to achieve (except very complex architecture).
If you want to share context on client you can try to pass around the instance context used for the very first proxy you created - I'm not sure if it will work but you can try it
If you want to share service instance context between multiple proxies you must develop your own IInstanceContextProvider and perhaps also your own IInstanceProvider (depending on what you want to achieve), wrap them in behavior and add them to the service. That will put whole complexity of session handling and correct instance releasing under your control (it obviously has its pros and cons).
But is it really needed? When I look at your code I see that one service and one proxy is enough. Also your JoinRoom operation doesn't need to use callback at all, it can be just request response method.
I have a service (Service1) that uses another serice (Service2). I am using Dependency injection for both services and need to inject the proxy for Service2 into Service1.
I am unsure how to deal with the fact that the proxy is not a simple class of type IService2 but a proxy inheriting from ClientBase as well. Obviously my Service1 implementation needs to open the proxy and should also close it after use or abort it if an exception occurs but if I am just injecting an instance of IService2 then I cannot do this (without casting) because the Open, Close and Abort methods are on the base class whilst my operations are on the interface.
When it comes to testing Service1, I would expect to mock just the interface but if the Service1 implementation expect Open, Close and Abort methods, then this is tricky. In the past, I have done something hacky like this but there must be a better way!
var proxyBase = _service2 as ClientBase;
if (proxyBase != null)
{
proxyBase.Open();
}
_service2.DoOperation("blah"); //the actual operation
if (proxyBase != null)
{
proxyBase.Close();
}
// repeat for Abort in exception handler(s).
What are other people doing?
Thanks
The auto generated class that you get for adding a service reference for a WCF service is implemented as a partial class. What I do is create a another partial file for that class and implement an interface that exposed those methods, and then use that interface where you would normally use the ClientBase or WCF interface
public partial class Service2 : IClientService2
{}
If IClientService2 has the Abort and Close methods that match the ClientBase methods it should be all you need.
public interface IClientService2 : IService2 // where IService2 is the WCF service interface
{
void Abort();
void Close();
}
I suggest injecting a factory to construct WCF services rather than injecting the proxy itself since when a fault occurs then the channel is no longer able to be used and you will need to construct a new proxy.
IClientService2 proxy = _service2Factory.Create();
proxy.Open();
proxy.DoOperation("blah"); //the actual operation
proxy.Close();
Your interface is poluted because of the requirements imposed by Wcf. If you were not using wcf you would not have an Open and Close method. In an ideal world the interface should look the same as if the service was in process.
Have you chosen your IoC container yet? If you haven't I would consider looking at Windsor. This will allow you to maintain a clean interface and either inject the service as an in process object or a wcf proxy.
container = new WindsorContainer().AddFacility<WcfFacility>();
container.Register(Component
.For<IClientService2>()
.ActAs(DefaultClientModel)
.On(WcfEndpoint.FromConfiguration("YourServiceNameInConfiguration")))
.LifeStyle.Transient);
The WcfFacility will do all the opening and closing of the channel for you.
I ended up using this approach which uses Castle Dynamic Proxy to intercept calls and handle WCF specifics. It works really well and allows the class where the proxy is injected to treat it like a normal class / interface. This class is then totally unit testable by mocking the service contract interface.
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.
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)
}
}
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".
In order to return an interface from a service you need to use the KnownType attribute:
http://weblogs.asp.net/avnerk/archive/2006/07/31/WCF-Serialization-part-1_3A00_-Interfaces_2C00_-Base-classes-and-the-NetDataContractFormatSerializer.aspx
If you want to return a custom type from the service:
http://msdn.microsoft.com/en-us/library/bb628653.aspx
Does any of that help?