0
votes

I have 4 WCF services that I have developed.

Each service is responsible for something else.

Each service has a UserNamePasswordValidator, so clients need to supply credentials when connecting.

I would now like to develop my WPF client application, in an MVVM architecture.

I would like for the WPF application to load with a 'Login' screen,

where the user will input a username and password,

and then this will be passed on to the 4 clients for the 4 WCF services (all use the same username and password).

What is the best approach to doing this ?

Where are the clients located ? in the 'Model' part ? which view's model ?

The WCF Service needs to be consumed by multiple views, so I don't think I can put any of the WCF service clients in a specific Model class...

1

1 Answers

1
votes

To do this, we have created a ServiceFactory class that connects to a service given its endpoint and an appropriate IClientChannel-derived interface. This assumes that you are using the WCF services directly, e.g. not via the VS-generated proxies, since you need to set the username and password values on each client channel creation.

The client channel interfaces are in an external "service library" along with the service factory, so they can be shared with the WCF service implementations and the clients. We store the credentials in a static state dictionary (though you also put it, for example, into the main resource dictionary) with the password being saved in a SecuredString for a tiny bit of extra security.

I've described the basic process for creating such a service factory on by blog:

http://blog.kutulu.org/2012/03/proxy-free-wcf-ditching-proxy.html

In our case, we perform a setup routine in App.xaml.cs that prompts for credentials and makes an attempt to call one of our services, looping until we get a successful login. That code is much more complex, but it's basically:

while (true)
{
  var factory = new ChannelFactory<ITestChannel>(new WSHttpBinding("SecuredBinding"));
  ITestChannel client = null;
  try
  {
    factory.Credentials.UserName.UserName = logOnModel.UserName;
    factory.Credentials.UserName.Password = logOnModel.Password;

    var address = Settings.Default.TestServiceUrlBase));
    client = factory.CreateChannel(address);

    break;
  }
  // Catch various exceptions here.
}

The trick here is that, if your login or password is wrong and your UsernamePasswordValidator fails your login, you'll get a MessageSecurityException which will fault your channel, so you'll need to close it and open a new one. But you cannot change the credentials on a ChannelFactory once you've opened the first channel, so you need to dispose and re-create a new factory and new client channel every time.

We also check for CommunicationException and ArgumentException here in case the URL is wrong.

Once that's done, you can use similar code in your service factory class to construct a client, given its channel interface, and set up the credentials for each call. (We actually cache the service factories for each distinct interface because we create and destroy channel frequently, but that's up to you.)