using System; // .NET Blog
Pour finir et en règle générale, n'oubliez pas qu'un service WCF peut avoir plusieurs points de communications. On peut donc très bien imaginer pour un même service de créer un point de communication avec une liason "basicHttpBinding" pour les clients SOAP 1.1 et un autre point avec une liaison "wsHttpBinding" pour les clients SOAP 1.2.
L'équipe Microsoft de WCF ainsi que des acteurs externes ont réalisé un livre blanc (actuellement en beta) sur les bonnes pratiques pour la mise
en place d'une architecture SOA sécurisée avec WCF. Un guide vraiment très complet que je vous recommande : WCF Security Guide.
Vous trouverez dans ce livre blanc plusieurs scénarios de systèmes sécurisés en spécifiant les meilleurs pratiques de sécurisation de vos services que ce soit au niveau de la tuyauterie WCF
via sa configuration ou de l'implémentation de ces services, gestion des identités utilisateurs, politique de sécurité d'accès aux bases de données via les services WCF...
Avant d'aller plus loin, pour ceux qui ne connaisent pas Unity Application Block, je vous renvoit vers mon post de Présentation d'Unity Application Block.
J'ai été confronté aujourd'hui à une problématique qui était de gérer un service générique. Nous utiliserons dans ce post pour exemple l'interface du service IServiceGenerique<T, U> avec son implémentation MonServiceGenerique<T, U>.
Nous utilisions alors toujours la version 1.0 d'Unity Application Block (grosse erreur !) et en faisant le code suivant pour récupérer nos services (en mode singleton via le
"ContainerControlledLifetimeManager") :
container.Resolve<IServiceGenerique<ClassA, ClassB>>();
container.Resolve<IServiceGenerique<ClassA, ClassA>>();
Unity nous récupérait alors bien le premier service IServiceGenerique<ClassA, ClassB> mais lors du deuxième Resolve, unity nous renvoyait toujours le
IServiceGenerique<ClassA, ClassB> au
lieu du IServiceGenerique<ClassA, ClassA> !
En faisant ensuite une recherche sur internet, c'était effectivement un bug d'unity v 1.0 qui a été corrigé avec unity 1.1. Si vous utilisez Unity, je vous
conseille donc vivement de mettre à jour vos dépendances en récupérant Unity V 1.1.
Avec Unity 1.1, le code suivant (toujours en mode singleton) :
container.Resolve<IServiceGenerique<ClassA, ClassB>>();
container.Resolve<IServiceGenerique<ClassA, ClassA>>();
container.Resolve<IServiceGenerique<ClassA, ClassA>>();
container.Resolve<IServiceGenerique<ClassB, ClassA>>();
nous retournera 3 instances de type IServiceGenerique<ClassA, ClassB>, IServiceGenerique<ClassA, ClassA> et IServiceGenerique<ClassB, ClassA>.
Pour information pour configurer l'enregistrement de votre type générique, voici la config
unity :
<type type="UAB.Demo.Interfaces.IServiceGenerique`2, UAB.Demo.Interfaces" mapTo="UAB.Demo.Services.MonServiceGenerique`2, UAB.Demo.Services">
<lifetime type="singleton" />
</type>
Pour une classe générique avec un seul type et non deux, remplacez le
'2 par '1 etc...
<
ProjectTypeGuids>{3D9AD99F-2412-4246-B90B-4EAA41C64699};
{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
Pour lancer en même temps l'outil "WCF Test Client" qui vous permet de tester votre service à partir d'une application windows, il suffit d'ajouter
la ligne suivante (toujours dans votre fichier de projet MsBuild) :
<StartArguments>/client:"WcfTestClient.exe"</StartArguments>
Il est souvent utile d'éditer directement son ficher de projet MsBuild notamment pour gérer ses tâches et imports MsBuild.
Lorsque votre fichier MsBuild est chargé, il est impossible de l'éditer directement dans Visual Studio. Il vous faut donc auparavant le décharger à partir du menu contextuel sur le projet en
sélectionnant "Unload Project" :
<system.serviceModel>
<services>
<service behaviorConfiguration="serviceBehavior" name="TradeSystem.Services.TradeService">
<endpoint address="ManagerTradeService" binding="basicHttpBinding" bindingConfiguration="" name="managerEndPoint" contract="TradeSystem.Interfaces.IManagerTradeService" />
<endpoint address="AnalystTradeSystem" binding="netTcpBinding" bindingConfiguration="" name="analystEndPoint" contract="TradeSystem.Interfaces.IAnalystTradeService" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:80"
/>
<add baseAddress="net.tcp://localhost:9000" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
L'un des problèmes de cette solution réside dans le fait où le client interroge les méta données du service, il récupéra aussi les opérations des deux acteurs (même
si suivant le endpoint utilisé, il ne pourra utiliser que certaines de ces opérations). Pour éviter cela, le moyen est donc d'exposer non pas un service mais deux
services.
public static class StringExtension
{
public static T EnumParse<T>(this string value)
where T : struct
{
return (T) EnumParse<T>(value, false);
}
public static T
EnumParse<T>(this string value, bool
ignoreCase)
where T : struct
{
return (T)Enum.Parse(typeof(T), value, ignoreCase);
}
}
Et pour l'utiliser, il vous suffira simplement de faire appel à la méthode "EnumParse<T>" à partir de vos types "string" :
string value = "Enum1";
MyEnum myEnum =
value.EnumParse<MyEnum>();
Le compilateur C# pour transformer votre méthode en méthode d'extension se contentera de positionner l'attribut "System.Runtime.CompilerServices.Extension" au-dessus de chaque méthode où le mot
clef "this" se trouve.
Restez tout de même vigilant dans l'utilisation de vos méthodes d'extensions. Une des plus grosses problématiques dans les méthodes d'extensions réside dans le fait de réaliser une méthode
"EnumParse<T>" et plus tard de se retrouver avec le framework 4.0 avec la même méthode. Du coup ca sera la méthode du framework qui sera appelé et non votre méthode d'extension. Pour éviter
cela, n'hésitez pas à préfixer vos méthodes d'extensions.
Nous verrons dans des prochains post l'utilisation des méthodes d'extensions au sein de Linq ainsi que la possibilité de surcharger vos méthodes d'extensions.
MyEnum myEnum1 = (MyEnum) Enum.Parse(typeof(MyEnum), "Enum1", true);
Créons à présent une classe "EnumHelper" permettant de gérer une méthode générique permettant d'arriver au même résultat mais en simplifiant la syntaxe :
public static class EnumHelper
{
public static T Parse<T>(string value)
where T : struct
{
return Parse<T>(value, false);
}
public static T
Parse<T>(string value, bool ignoreCase)
where T : struct
{
return (T)Enum.Parse(typeof(T), value, ignoreCase);
}
}
Avec notre classe "EnumHelper", nous obtenons donc :
MyEnum myEnum2 = EnumHelper.Parse<MyEnum>("Enum1");
Nous verrons dans un prochain post comment utiliser la fonctionnalité des méthodes d'extension en C# 3.0 pour ajouter une méthode sur le type string permettant de le transformer en une
énumération.
Le fait de ne pas pouvoir affecter la valeur null sur des types valeurs en .NET 1.0 pouvait poser problème. Il est en effet impossible
d'affecter la valeur null à un type int par exemple comme suit :
int number = null;
La solution était tout simplement alors d'affecter par exemple une valeur arbitraire pour désigner la valeur null (int.MinValue par exemple) ou encore de créer une structure wrapper sur le type pour indiquer via un booléen si le type était null.
C'est ce dernier choix qui a été fait avec l'arrivée de C# 2.0 avec le type Nullable<T> qui permet donc d'affecter la valeur null sur un type valeur et seulement valeur. En effet, une contrainte sur le type T de la classe Nullable<T> a été placé pour ne pouvoir passer que des types valeurs (un type par référence
n'ayant aucune raison d'être géré).
Pour affecter la valeur null sur le type int, nous allons donc avoir :
Nullable<int> number = null;
A savoir que toujours depuis C# 2.0, l'opérateur "?" placé devant le type valeur va vous permettre de faire appel directement au type Nullable<T>. Le code peut donc être remplacé par le suivant :
int? number = null;
Deux moyens seront mis alors à votre disposition pour pouvoir tester la nullité de votre type valeur. Le premier en faisant appel à la propriété "HasValue" et le deuxième en testant tout
simplement l'égalité sur null comme suit :
if (!number.HasValue)
return;
if (number == null)
return;
Il faut ensuite faire attention à la manipulation de ces types nullables. Il vous sera impossible par exemple d'avoir la ligne de code suivante :
int number2 = number.Value + 2;
...pour la simple raison que le compilateur ne peut convertir un type int? en int. Deux moyen là encore seront mis à votre disposition. Le premier est de déclarer le type number2 en type nullable
:
int ? number2 = number + 2;
Le deuxième est d'utiliser la propriété "Value" du type nullable :
int number2 = number.Value + 2;
Concernant le boxing, il vous sera possible de boxer un type seulement si celui-ci est nullable. Nous pourrions donc avoir :
object o = 33;
int? number = (int?)o;
Derniers Commentaires