Reputation: 179
The problem is that contrary to controllers in MVC, MVVM models in WPF are instantiated and are there for good. Practically what that means for me is that if I have a private property in my viemodel my proxy will be open for a long time eg.:
//Some example after googling for "consuming wcf services in wpf app~"
private FootballerServices.FootballerServiceClient footballersService = null;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
footballersService = new FootballerServices.FootballerServiceClient();
try
{
FootballerBox.ItemsSource = footballersService.GetFootballers();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
How do I solve that issue properly?
First solution:
private void button_Click(object sender, RoutedEventArgs e)
{
MyServicesClient proxy = new MyServicesClient();
try
{
MyServicesData data = proxy.GetDataFromMyService();
proxy.Close();
}
catch (FaultException ex)
{
MessageBox.Show("Fault Exception: " + ex.Message);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Then I could make a new class like ServiceWrapper where I would encapsulate all the method calls: private void button_Click(object sender, RoutedEventArgs e) { var t = serviceWrapper.GetDataFromMyService(); (...) }
and in Service Wrapper:
private void button_Click(object sender, RoutedEventArgs e)
{
MyServicesClient proxy = new MyServicesClient();
try
{
MyServicesData data = proxy.GetDataFromMyService();
proxy.Close();
}
catch (FaultException ex)
{
(...)
}
catch (Exception ex)
{
(...)
}
return data;
}
Now the last thing would be to add DI to that solution
Second Solution
Another way that I found is to use Abstract Factory pattern with DI like it was answered here:
I do not fully understand this solution, however. My understanding was that it happens like this: DI resolves an instance of my ServiceClient and after execution goes out scope of the method that used the resolved instance, the DI container disposes of that instance - that there is no need for Abstract factory pattern. Apparently, my understanding was wrong.
Question: What is the proper practice in such case?
Upvotes: 0
Views: 1740
Reputation: 29262
You most likely wouldn't want to inject a WCF client because you only need it for a very short time. What you can inject is a factory that will return the service when needed.
The factory interface may look like this:
public interface IMyServiceFactory
{
IMyService Create();
void Release(IMyService created);
}
Assume that you're injecting this through your constructor and adding a private readonly field to your class, like this:
private readonly IMyServiceFactory _myServiceFactory;
Then when you need the service, you do this:
var myService = _myServiceFactory.Create();
try
{
// do something with the service
}
finally
{
_myService.Release(myService);
}
A big benefit is that your class depends entirely on abstractions. It doesn't "know" that the service is implemented by a WCF client or that the factory is calling a DI container. You can test it using a mock of IMyService
. That's impossible if your class is directly creating its own WCF client.
You didn't mention which container you're using, but many of them will create WCF clients for you to implement interfaces.
Another nice detail about Windsor is that it will also create the abstract factory for you.
Here's a few more pieces to make up a sample implementation using Windsor. This supposes that you have a WCF service that implements IMyService
.
Here's the service interface, the factory interface, and the class that consumes it:
public interface IMyService
{
IEnumerable<string> GetSomeData();
}
public interface IMyServiceFactory
{
IMyService Create();
void Release(IMyService created);
}
public class ClassThatConsumesService
{
private readonly IMyServiceFactory _serviceFactory;
public ClassThatConsumesService(IMyServiceFactory myServiceFactory)
{
_serviceFactory = myServiceFactory;
}
public void MethodThatDoesSomething()
{
var service = _serviceFactory.Create();
try
{
var data = service.GetSomeData();
// do whatever
}
finally
{
_serviceFactory.Release(service);
}
}
}
Then here's an example of some implementation details using Windsor. But one important detail is that nothing above depends on Windsor. You could do this using a different DI container. You don't even need a DI container at all. All your class knows is that it's calling a factory to get an instance of a service.
public class ClassThatConsumesService
{
private readonly IMyServiceFactory _serviceFactory;
public ClassThatConsumesService(IMyServiceFactory myServiceFactory)
{
_serviceFactory = myServiceFactory;
}
public void MethodThatDoesSomething()
{
var service = _serviceFactory.Create();
try
{
var data = service.GetSomeData();
// do whatever
}
finally
{
_serviceFactory.Release(service);
}
}
}
To use Windsor you would add the Windsor WCF Facility nuget package, which is Windsor plus some additional classes for handling WCF services.
Your container registration might look like this:
container.AddFacility<TypedFactoryFacility>();
container.AddFacility<WcfFacility>();
container.Register(Component.For<IMyService>()
.AsWcfClient(WcfEndpoint.FromConfiguration("MyServiceEndpointName")));
container.Register(Component.For<IMyServiceFactory>().AsFactory());
This does the following:
IMyService
is a WCF client using the endpoint specified in app.config/web.config named "MyServiceEndpointName".IMyServiceFactory
is a factory that Windsor creates. Here's that documentation again.. What that means is that you don't actually create a class to implement IMyServiceFactory
. The container does it. When you call Create
the factory creates the client. When you call Release
it disposes the client.If you wanted to you could write your own factory implementation, like this:
public class WcfClientMyServiceFactory : IMyServiceFactory
{
public IMyService Create()
{
return new MyServiceClient();
}
public void Release(IMyService created)
{
var client = (MyServiceClient) created;
try
{
try
{
client.Close();
}
catch
{
client.Abort();
}
}
finally
{
client.Dispose();
}
}
}
(Don't quote me on the details of how to properly close a WCF client. It's been a while and I'm rusty.)
But having gotten used to using Windsor it's just a lot easier.
Having done it this way, what if you want to unit test your class assuming that IMyService
will return specific data? Now it's really easy to use Moq or just write test double classes like this:
public class MyServiceDouble : IMyService
{
public IEnumerable<string> GetSomeData()
{
return new [] {"x", "y", "z"};
}
}
public class MyServiceFactoryDouble : IMyServiceFactory
{
public IMyService Create()
{
return new MyServiceDouble();
}
public void Release(IMyService created)
{
// Nothing to clean up.
}
}
Because your class doesn't know anything about IMyService
- it doesn't know that the "normal" implementation is a WCF client - it's easy to replace it with something else.
Upvotes: 3