Unity et WCF
Je rebondis sur le billet de Thomas à propos de WCF et de la résolution du proxy WCF par Unity (ou tout autre conteneur).
Prenons cet exemple basique de service:
[ServiceContract]
public interface ICalc
{
[OperationContract]
int Add(int a, int b);
[OperationContract]
int Div(int a, int b);
}
Dans cet exemple une division par zéro va produire une exception qui aurait du être géré par un FaultContract au niveau de l’opération. La conséquence est fatale pour le proxy client: le “channel” passe en état “Faulted” et plus aucune opération est possible. Si on laisse le soin à une bibliothèque tierce de créer le proxy, si la stratégie de création est le singleton, on est bloqué à la première erreur coté serveur.
Alors comment savoir si le “channel” est dans le bon état?On ne le peut pas si on n’a que l’interface du contrat comme lien avec le proxy comme dans l’exemple de Thomas. On a alors généralement 2 solutions:
- Le proxy fournit une propriété Channel
- Si l’on ne passe pas par le proxy, on peut passer par une interface un peu plus évoluée que l’interface contrat.
L’interface suivante est crée par le générateur de proxy de Visual Studio (mais on aurait pu l’écrire directement):
public interface ICalcChannel : Client_ConfByConfig.CalcSvc.ICalc, System.ServiceModel.IClientChannel {
}
Il suffit ensuite de créer un ChannelFactory<ICalcChannel> et nous avons un proxy créé avec nos méthodes et l’accès au “Channel” via l’interface “IClientChannel”.
Dans ce cas, c’est la factory de WCF qui va faire le boulot pour nous: donc plus de classe cliente. Pour utiliser cela avec Unity, nous allons utiliser un LifetimeManager. Nous allons donner à une classe intermédiaire la responsabilité de créer le proxy pour nous.
Et pour WCF cela peut ressembler à cela:
internal class WCFLifetimemanager<t> : LifetimeManager, IDisposable where T : IDisposable
{
ChannelFactory _factory;
public WCFLifetimemanager()
: this("*")
{
}
public WCFLifetimemanager(string endpoint)
{
_factory = new ChannelFactory<t>(endpoint);
}
public override object GetValue()
{
var ret = _factory.CreateChannel();
return ret;
}
public override void RemoveValue()
{
throw new InvalidOperationException();
}
public override void SetValue(object newValue)
{
throw new InvalidOperationException();
}
#region IDisposable Members
public void Dispose()
{
if (_factory != null)
{
_factory.Close();
}
}
#endregion
}
Coté enregistrement, c’est aussi simple qu’avec un proxy:
container.RegisterType<ICalcChannel>(new WCFLifetimemanager<ICalcChannel>());
Nous pouvons écrire cela maintenant:
var calc = container.Resolve<ICalcChannel>();
if (calc.State == System.ServiceModel.CommunicationState.Opened)
{
...
}
Il est toujours possible de passer par la conf pour faire cette opération, mais si l’on veut passer par code, on peut encore un peu se simplifier la vie par une méthode d’extension:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Client_ConfByCode;
namespace Microsoft.Practices.Unity
{
public static class UnityContainerExtensions
{
///
/// Register a service in the given UnityContainer for the specified endpoint
///
///
///
///
///
public static IUnityContainer RegisterService<T>(this IUnityContainer container, string endpoint) where T : IDisposable
{
return container.RegisterType<T>(new WCFLifetimemanager<T>(endpoint));
}
///
/// Register a service in the given UnityContainer with the "*" endpoint
///
///
///
///
///
public static IUnityContainer RegisterService<T>(this IUnityContainer container) where T : IDisposable
{
return container.RegisterType<T>(new WCFLifetimemanager());
}
}
}
Et l’enregistrement devient encore plus simple:
ret.RegisterService<ICalCchannel>();
Les sources du billet sont ici.
A+
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :