Blog du Pôle .NET

Partager pour mieux développer...

WCF : CommunicationException au lieu de FaultException[T]

clock October 21, 2009 04:17 by author Xavier Masclet

J’ai récemment voulu mettre en place une gestion d’exceptions « WCF-compliant » sur un service WCF.

L’idée étant la suivante : le service WCF doit attraper toutes les BusinessException susceptibles d’être levées par la couche métier et les transformer en FaultException<BusinessFault>

J’ai donc mis en place un handler d’exception qui implémente l’interface IErrorHandler ainsi qu’un « comportement de service » (une classe implémentant IServiceBehavior en fait).

Parallèlement à ça j’ai défini un contrat « BusinessFault » et fait en sorte que mes exceptions métier (BusinessException) puissent fournir la BusinessFault correspondante.

 

Voici le code de la BusinessException :

 

public class BusinessException : Exception

{

    public void TranslateToFault(MessageVersion version, ref Message faultMessage)

    {

        string inner = (InnerException == null ? "" : InnerException.GetType().Name);

        BusinessFault tf = new BusinessFault(this.Code, this.Message, inner);

        MessageFault f = MessageFault.CreateFault(new FaultCode("Receiver"), new FaultReason("Erreur métier"), tf);

        erreur = System.ServiceModel.Channels.Message.CreateMessage(version, f, "defaultAction");

    }

 

}

 

BusinessFault est une simple classe marquée comme [DataContract] et possédant 3 accesseurs de type string marqués comme [DataMember].

 

Ci-après le code du handler d’exception :

 

public sealed class MyExceptionHandler : IErrorHandler

{

    bool IErrorHandler.HandleError(Exception ex)

    {

        return true;

    }

 

    void IErrorHandler.ProvideFault(Exception ex, MessageVersion version, ref Message fault)

    {

        BusinessException exception = ex as BusinessException;

        if (exception != null)

        {

            exception.TranslateToFault(version, ref fault);

        }

        else

        {

            HandleUnknownException(ex, version, ref fault);

        }

    }

 

    private void HandleUnknownException(Exception exception, MessageVersion version, ref Message erreur)

    {

        // ...

    }

}

 

Et maintenant le behavior qui permet de transformer automatiquement toutes les BusinessExceptions en FaultException :

 

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]

public sealed class MyExceptionHandlingBehaviorAttribute : Attribute, IServiceBehavior

{

    void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)

    {

    }

 

    void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)

    {

        if (serviceHostBase == null)

        {

            return;

        }

 

        // Ajout du handler d'exception sur tous les dispatchers WCF

        foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)

        {

            dispatcher.ErrorHandlers.Add(new MyExceptionHandler());

        }

    }

 

    void IServiceBehavior.Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)

    {

    }

}

 

 

Pour que tout ceci fonctionne bien, il restait à décorer les méthodes de service comme suit :

 

[FaultContract(typeof(BusinessFault)]

PersonneDataContract EnregistrerPersonne(PersonneDataContract personneDataContract);

 

Et enfin décorer la classe d’implémentation de service avec l’attribut précédemment créé :

 

[MyExceptionHandlingBehaviorAttribute]

public class MyService : IMyService

{

}

 

Et voilà. “Normalement” tout doit fonctionner…

En effet ca fonctionne bien… tant que le endpoint de mon service utilise du basicHttpBinding !

Dès que l’on passe en wsHttpBinding, ca ne fonctionne plus, en effet coté client au lieu de récupérer une FaultException<BusinessFault>, je récupère une CommunicationException, bof bof !

Pour corriger cela, il est en fait nécessaire de spécifier une action sur la fault fournie par le service et il faut aussi spécifier cette même action sur les méthodes de service. Ainsi il suffit de définir une constante FaultAction, par exemple :

 

public class Constantes

{

    public const string MyFaultAction = "http://MyService/Fault";

}

 

Ensuite il faut modifier notre méthode TranslateToFault (sur la classe BusinessException) comme ceci :

 

public void TranslateToFault(MessageVersion version, ref Message faultMessage)

{

    string inner = (InnerException == null ? "" : InnerException.GetType().Name);

    BusinessFault tf = new BusinessFault(this.Code, this.Message, inner);

    MessageFault f = MessageFault.CreateFault(new FaultCode("Receiver"), new FaultReason("Erreur métier"), tf);

    erreur = System.ServiceModel.Channels.Message.CreateMessage(version, f, Constantes .MyFaultAction);

}

 

Notez l’utilisation de la constante dans l’appel de la méthode Message.CreateMessage, pour le paramètre « Action ».

 

Enfin il reste à spécifier cette action sur la méthode de service qui devient donc :

 

[FaultContract(typeof(BusinessFault), Action = Constantes.MyFaultAction)]

PersonneDataContract EnregistrerPersonne(PersonneDataContract personneDataContract, ErrorManager errorManager);

 

Une fois régénéré le proxy coté client, ce dernier est maintenant en mesure de catcher correctement des FaultException<BusinessFault> levées par le service.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Lenteur firefox sous Vista / Seven

clock April 8, 2009 10:46 by author Xavier Masclet
Certains auront remarqué que firefox est particulièrement long quand on dev en local sur un poste qui tourne sous Vista ou Seven.
L'astuce est la suivante, et m'a fait gagné 14 secondes sur le temps de chargement d'une page :

- Dans Firefox ouvrez un nouvel onglet
- Dans la barre d'adresse tapez : about:config
- Cliquez sur le bouton "I'll be careful, I promise"
- dans le filtre saisissez : ipv6
- double cliquez sur la ligne network.dns.disableipv6 pour mettre la valeur à 'true'.
- Redémarrez firefox

Voila c'est fait. Maintenant ca devrait aller beaucoup plus vite.
Cette astuce n'est valable que lorsqu'on fait tourner un site en local.

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Créer un Dictionary[string, List[T]] en LinqToObject ...

clock February 20, 2009 08:23 by author Xavier Masclet

.... ou comment créer un dictionnaire de "regroupement" a partir d'une liste simple.

Au départ on a en entrée : List<Communes> lesCommunes.

Et on voudrait créer un dictionnaire de ces meme communes ayant pour clé leur code postal. MAIS.... plusieurs communes pouvant avoir le meme code postal, on souhaiterait avoir en valeur non pas une Commune, mais une liste de communes. Autrement dit :

> Dictionary<string, List<Commune>>

 Ceci se fait via la requête suivante en utilisant la méthode d'extension ToDictionary<TKey, TSource>(...) :

var rqt = from c in lesCommunes
              group c by c.CodePostal into communes_par_CP
              select new { list = communes_par_CP };

Dictionary<string, List<Commune>> lesCommunesParCodePostal = rqt.ToDictionary(a => a.list.Select(b => b.CodePostal).First(), c => c.list.Select(y => y).ToList<Commune>());

 Et tant qu'à faire, autant pousser jusqu'au SortedDictionary....:

SortedDictionary<string, List<Commune>> lesCommunesParCodePostal = new SortedDictionary<string, List<Commune>>(rqt.ToDictionary(a => a.list.Select(b => b.CodePostal).First(), c => c.list.Select(y => y).ToList<Commune>()));

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Héritage de classe générique sur une Window

clock January 21, 2009 04:38 by author Xavier Masclet
Par défaut en WPF les formulaires héritent de la classe System.Windows.Window.
Or il est peut être interessant de les faire hériter d'une classe tierse dans laquelle on factorise un certain nombre de choses (cette classe héritant elle même de System.Windows.Window pour que tout fonctionne bien).

Dans le cas de l'héritage d'un type simple il suffit de procéder comme suit :

1. On définit un type de base (à ajouter dans l'assembly contenant les formulaires ou dans une assembly connue de cette dernière)

namespace IHM.Common
{
   public class MyBaseWindow : Window

   {
      //...
   }
}

2. Il faut faire hériter notre formulaire de ce type

 - Coté code :

using IHM.Common;
namespace IHM.UI
{
   public class Window1 : MyBaseWindow
   {
      //...
   }
}

 - Et coté XAML : (et oui ! Le compilateur nous y oblige sous peine d'une erreur "Partial declarations of 'IHM.UI.Window1' must not specify different base classes")


   <src:MyBaseWindow
        x:Class="IHM.UI.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:src="clr-namespace:IHM.Common"
        Title="Une Fenetre"
        Height="300"
        Width="300">
   ...
   </src:MyBaseWindow>


Comment faire si maintenant nous souhaitons hériter d'une classe générique ?
En l'occurrence :

namespace IHM.Common
{
   public class MyBaseWindow<T> : Window

   {
      //...
   }
}

1. Coté code pas de soucis :

using IHM.Common;
using Rules;

namespace IHM.UI
{
   public class Window1 : MyBaseWindow<MyRule>
   {
      //...
   }
}

2. Coté XAML, petite subtilité avec l'utilisation de la propriété "TypeArguments" qui permet de spécifier le type générique à utiliser :

   <src:MyBaseWindow
        x:Class="IHM.UI.Window1"
        x:TypeArguments="r:MyRule"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:src="clr-namespace:IHM.UI"
        xmlns:r="clr-namespace:Rules;assembly=Rules"
        Title="Recherche de personnes"
        Height="300"
        Width="300">
   </src:MyBaseWindow>

Notez la déclaration du namespace : xmlns:r="clr-namespace:Rules;assembly=Rules" (préfixé par la lettre 'r')
qui permet au compilateur de retrouver notre type 'MyRule' dans la spécification du type générique : x:TypeArguments="r:MyRule"

Currently rated 4.3 by 3 people

  • Currently 4.333333/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Calendrier

<<  July 2010  >>
MoTuWeThFrSaSu
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678
Afficher en pleine page

Visiteurs