Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Frédéric Hamel

Assert.Success();

[WPF] Réglons son compte à INotifyPropertyChanged (dérivation, réflexion, AOP, EntLib, Spring.NET, extension)

Introduction

INotifyPropertyChanged est une interface classique pour notifier à un client qu'une propriété a changé. C'est un scénario typique en WPF quand on utilise du Binding. Je vais vous présenter plusieurs techniques pour l'implémenter et discuter des problèmes que l'on peut rencontrer.

Voilà le sommaire de ce post :

  • Introduction
  • Version élémentaire
  • Version avec une classe de base
  • Version avec une classe de base par Josh Smith
  • Bilan de mi-parcours
  • Version sans création de PropertyEventArgs à chaque fois
  • AOP interception et injection
  • Entreprise Library, Policy Injection Application block
  • Spring.NET and AOP
  • La solution .NET 3.5 via extension methods
  • Optimisation possible
  • Conclusion

Et voilà la classe qui va nous suivre tout au long de ce post :

public class Person : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private string _name;
    public string Name { 
        get { return _name; }
        set { _name = value; }
    }
}

La classe Person dérive de INotifyPropertyChanged et l'implémente grâce à l'événement PropertyChanged.

Version élémentaire

On rajoute la fonction suivante qui permet de déclencher l'événement :

private void RaisePropertyChanged(string propertyName) 
{
    if (PropertyChanged == null) return;
    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}

Et on modifie le set de la propriété, comme cela, pour déclencher l'évènement :

set 
{ 
    _name = value;
    RaisePropertyChanged("Name");
}

C'est la méthode la plus simple et c'est celle que je me vois implémenter le plus souvent.

Discutons un peu cette méthode :

Les Pour :

  • Simple
  • Efficace

Les Contre :

  • Copier/coller de la fonction RaisePropertyChanged pour chaque classe
  • Utiliser la string "Name" pose des problèmes de maintenance car une faute de frappe est vite arrivée et il est souvent difficile de débugger ce genre d'erreur.
  • Création d'un objet PropertyChangedEventArgs à chaque set de la propriété : Josh Smith mentionne dans un de ses posts que cette création de petits objets peut risquer de fragmenter la mémoire managée.
  • C'est relativement fastidieux si l'on a beaucoup de propriétés et/ou beaucoup d'objets.

Version avec une classe de base

Le problème du copier/coller de la fonction RaisePropertyChanged peut être résolu en utilisant une classe de base :

public class NotifyPropertyChangedBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged == null) return;
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

Les Pour :

  • Les mêmes que pour la version précédente
  • Il n'y a plus de copier/coller de la fonction ni de l'événement

Les Contre :

  • A part le copier/coller les problèmes sont les mêmes
  • Dériver d'une classe de base n'est pas toujours possible à cause de l'héritage simple en C# ou VB.NET

Version avec une classe de base par Josh Smith

Josh Smith a écrit une classe de base aussi pour ce problème ici

Les principaux avantages de sa classe sont que le nom de la propriété est vérifié via réflexion et que les événements sont mis en cache.

Les Pour :

  • Vérification du nom de la propriété par réflexion
  • Mise en cache des PropertyChangedEventArgs
  • La vérification n'est faite qu'en mode débug, donc pas de perte de performance en mode release

Les Contre :

  • L'implémentation est lente, beaucoup plus que de recréer l'objet à chaque fois
  • Encore une fois cela oblige à dériver d'un objet
  • Il est toujours fastidieux d'écrire le RaisePropertyChanged pour chaque setter

Bilan de mi-parcours

On voit que malgré trois propositions d'implémentation, il reste toujours un certain nombre de problèmes plus ou moins gênants. Pour le moment, nous n'avons abordé que des solutions classiques, c'est-à-dire implémentation directe ou via dérivation. Nous avons aussi commencé à voir que via réflexion, on peut valider le nom de la propriété. Par la suite, je vais vous présenter une version optimisée pour la vitesse et d'autres solutions moins classiques.

Version sans création du PropertyChangedEventArgs à chaque fois

Dans la version de Josh, la création de l'évènement est remplacé par un lookup dans un dictionnaire pour récupérer la même instance de l'argument à chaque fois. C'est de là que vient la lenteur de la méthode. On peut, simplement, arriver à une méthode rapide et qui évite la fragmentation :

public class Person : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private void RaisePropertyChanged(PropertyChangedEventArgs eventArgs)
    {
        if (PropertyChanged == null) return;
        PropertyChanged(this, eventArgs);
    }

    private static readonly PropertyChangedEventArgs _nameChangedEventArgs
        = new PropertyChangedEventArgs("Name");

    private string _name;
    public string Name { 
        get { return _name; }
        set 
        {
            _name = value;
            RaisePropertyChanged(_nameChangedEventArgs);
        }
    }
}
 

Ici, la ruse est simple : on crée un singleton de l'argument et on le passe directement à la méthode RaisePropertyChanged. Grâce aux mots-clés static et readonly, le runtime .NET assure la création unique de l'objet.

Les Pour :

  • Simple
  • Très efficace
  • Pas de fragmentation de la mémoire

Les Contre :

  • Toujours les mêmes problèmes que pour la version élémentaire (à part la fragmentation)

AOP interception et injection

L'AOP (Aspect Oriented Programming) est un paradigme de programmation qui prône la séparation des considérations techniques et des descriptions métier dans une application. Ici, on est tout à fait dans ce cas. Je dois dire qu'en ce moment je suis très intéressé par ce paradigme de programmation.

Il existe beaucoup de frameworks permettant de faire de l'AOP en .NET et encore beaucoup plus dans le monde Java. Je vais en citer deux:

Je vais présenter une méthode sur Entreprise Library et son module de Policy Injection, et une pour Spring.NET.

Entreprise Library, Policy Injection Application Block

Le Policy Injection Application Block de Entreprise Library permet de construire ou d'envelopper un objet avec un proxy, qui va pouvoir intercepter les appels de méthodes et de propriétés et réaliser des actions avant ou après. Les injections de bases fournies par Entlib et Spring sont par exemple le log et la gestion d'exception, ou encore la mise en cache. Pour les deux frameworks, il faudra écrire le petit bout de code qui fait la notification.

De plus, l'objet aura une contrainte suplémentaire : il devra implémenter une interface ou dériver de la classe MarshalByRefObject. Tout ceci est très bien expliqué dans la documentation d'Entlib. Concentrons-nous donc sur l'implémentation de INotifyPropertyChanged.

Pour commencer, on va créer un handler qui vas permettre de gérer l'interception de la propriété :

public class NotifyPropertyChangedHandler : ICallHandler
{
    public IMethodReturn Invoke(IMethodInvocation input,
                                 GetNextHandlerDelegate getNext)
    {
        //Before the call

        IMethodReturn result = getNext()(input, getNext);

        //After the call
    }

    public int Order{
        get{ return 0; }
        set{ throw new NotImplementedException(); }
    }
}

Pour créer le handler, il suffit de dériver de ICallHandler qui se trouve dans Microsoft.Practices.EnterpriseLibrary.PolicyInjection. L'interface comprend la méthode Invoke et la propriété Order. La méthode Invoke, comme son nom l'indique, va représenter l'appel de la méthode ; la propriété Order va être utile dans le cas où il y a plusieurs handlers pour la même méthode.

IMethodReturn result = getNext()(input, getNext);

Cette ligne représente l'appel de la propriété ou de la fonction. Tout ceci est détaillé dans la documentation d'Entlib. Ce qu'il faut comprendre, c'est que le code que l'on met avant s'éxécutera avant, et celui qu'on met après... ben après ! Pour nous, c'est la partie après qui nous intéresse.

Dans la variable input on trouve les deux propriétés qui vont nous servir :

  • input.MethodBase.Name
  • input.Target

Le premier point nous permet d'avoir le nom de la propriété et le deuxième l'objet que l'on est en train d'intercepter.

Maintenant, reste à savoir quoi mettre après l'appel pour déclencher l'événement. On pense tout de suite à la réflexion, mais après avoir cherché un peu, ce n'est pas si facile qu'il y parait. Si on récupère l'événement via GetEvent, on obtient un EventInfo qui n'a pas l'air de permettre de lancer l'événement. En continuant mes recherches, j'ai trouvé une solution via le field qui permet de récupérer le PropertyChangedEventHandler. Voilà le code commenté :

public class NotifyPropertyChangedHandler : ICallHandler
{
    public IMethodReturn Invoke(IMethodInvocation input,
                                 GetNextHandlerDelegate getNext)
    {
        //Before the call

        IMethodReturn result = getNext()(input, getNext);

        //Check if it's a get or a set
        if (!input.MethodBase.Name.StartsWith("set_")) return result;

        //Get the field that contains the event handler
        var propertyChangedField =
            input.Target.GetType().GetField("PropertyChanged",
            BindingFlags.Instance | BindingFlags.NonPublic);

        var eventHandler = propertyChangedField.GetValue(input.Target)
            as PropertyChangedEventHandler;

        if (eventHandler == null) return result;

        //Get the name of the property by removing set_
        var propertyName = input.MethodBase.Name.Remove(0, 4);
        //Job done invoke the event
        eventHandler.Invoke(input.Target, 
            new PropertyChangedEventArgs(propertyName));

        return result;
    }

    public int Order{
        get{ return 0; }
        set{ throw new NotImplementedException(); }
    }
}

Pour se simplifier la vie, l'idéal est de rajouter un attribut qui permet de marquer les propriétés que l'on veut intercepter :

[AttributeUsage(AttributeTargets.Property)]
public class NotifyPropertyChangedAttribute : HandlerAttribute
{
    public override ICallHandler CreateHandler()
    {
        return new NotifyPropertyChangedHandler();
    }
}

Voilà, il ne reste plus qu'à marquer la classe Person et à utiliser l'injecteur pour créer le proxy :

public class Person : IPerson, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private string _name;
    [NotifyPropertyChanged]
    public string Name { 
        get { return _name; }
        set 
        {
            _name = value;
        }
    }
}

On notera aussi que j'ai fait une interface IPerson car pour créer le proxy, il faut soit une interface, soit un MarshalByRefObject.

Voilà finalement le code qui créera le wraper et qui déclenchera le handler pour les propriétés :

var person = new Person();
person.PropertyChanged += 
    (sender, e) => Console.WriteLine(e.PropertyName);

var wrapedPerson = PolicyInjection.Wrap<IPerson>(person);
wrapedPerson.Name = "Frédéric Hamel";

Je pense que c'est une méthode très intéressante à cause des concepts qu'elle met en jeu, car cela peut permettre de résoudre de nombreux problèmes plus complexes.

Donc pour résumer :

Les Pour :

  • Pas de copier/coller
  • Réutilisation du code flexible
  • Récupération du nom de la propriété automatique

Les Contre :

  • Complexe la 1ère fois qu'on le fait
  • Introduit une dépendance dans le projet au framework d'injection
  • L'objet est wrapé dans un proxy
  • Oblige à avoir une interface ou à dériver de MarshalByRefObject

Spring.NET and AOP

Avec Spring.NET, il est possible d'aller plus loin qu'avec Entreprise Library. En effet, avec Spring.NET, on peut carrément mixer l'objet cible avec l'objet qui contient les mécanismes à insérer. Donc, avec Spring, il suffit de créer une classe qui va implémenter INotifyPropertyChanged, et de la mixer avec l'objet business.

Regardons le code qui permet de créer le proxy et voyons ensuite les classes à créer pour que cela fonctionne.

var factory = new ProxyFactory(new Person());

factory.AddIntroduction(advisor);
factory.AddAdvice(advisor.AfterAdvice);

factory.ProxyTargetType = true;

var person = (Person)factory.GetProxy();

On commence par créer une factory et on lui passe l'objet dont on souhaite avoir le proxy. Ensuite, on ajoute à la factory une introduction. Une introduction est la classe que l'on va mixer avec l'objet business. Une fois les deux objets mixés, l'objet business implémentera INotifyPropertyChanged : il ne reste donc plus qu'à écrire l'interception des setters pour pouvoir invoquer l'événement.

Pour cela, on ajoute une advice. Celle que j'ai choisie est l'AfterReturningAdvice. En effet, cette advice est particulièrement bien adaptée à notre situation car elle s'exécute après que la méthode ait retourné sa valeur. Pour finir, on met la propriété ProxyTargetType à true pour que l'on puisse caster le proxy dans le type d'origine et on appelle GetProxy qui va nous fabriquer tout cela.

Voyons maintenant l'objet à introduire :

public class NotifyPropertyChangedMixin : 
    INotifyPropertyChanged, 
    ITargetAware, 
    IAfterReturningAdvice
{
    public event PropertyChangedEventHandler PropertyChanged;

    private void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            //Here you can use TargetProxy or the target
            //argument of the AfterReturning method
            PropertyChanged(TargetProxy, 
                new PropertyChangedEventArgs(propertyName));
        }
    }

    //The proxy
    public IAopProxy TargetProxy
    {
        private get;
        set;
    }

    //This method will be called after the method returned
    public void AfterReturning(
        object returnValue, 
        MethodInfo method, 
        object[] args, 
        object target)
    {
        //We check that we have a setter
        if (method.Name.StartsWith("set_") 
            && (method.GetParameters().Length == 1)) 
        {
            RaisePropertyChanged(method.Name.Remove(0, 4));
        }
    }

}

La classe dérive de INotifyPropertyChanged et l'implémente avec la technique élémentaire que l'on a vu au début du post. La classe implémente aussi ITargetAware qui est une interface fournie par le framework Spring et qui permet de savoir dans quel proxy l'on se trouve. C'est une interface optionnelle, mais ici je l'ai implémentée pour pouvoir récupérer le proxy.

Le private get a été rajouté par mes soins car il n'est pas nécessaire non plus pour l'interface ITargetAware.

La dernière interface IAfterReturningAdvice permet d'avoir la méthode AfterReturning qui, comme son nom l'indique, va nous permettre d'éxécuter du code après le retour de la méthode cible.

La méthode AfterReturning est ultra simple : elle vérifie juste que l'on a une propriété et déclenche l'événement.

J'aime bien la manière dont les interfaces sont nommées dans Spring. En effet, c'est très imagé : l'advisor (le professeur) va donner des advices (des conseils) à la factory, et ces advices vont s'appliquer au moment de l'exécution. Voila le advisor que j'ai réalisé :

public class NotifyPropertyChangedAdvisor 
    : DefaultIntroductionAdvisor
{
    public NotifyPropertyChangedAdvisor()
        :base(new NotifyPropertyChangedMixin()) {}

    public IAfterReturningAdvice AfterAdvice
    {
        get {
            return (IAfterReturningAdvice)base.Advice;
        }
    }
}

On voit qu'une implémentation par défaut est donnée par Spring et on lui passe juste la classe à mélanger. J'ai aussi rajouté une propriété pour récupérer le conseil.

Normalement, les classes à mélanger et les advices sont des objets différents. Ici, j'ai choisi de tout mettre dans NotifyPropertyChangedMixin, car je trouvais que c'était mieux du point de vue de l'encapsulation. En effet, comme l'advice est dans la même classe, elle peut déclencher l'événement sans avoir recours à la réflexion qui viendrait quand même court-circuiter l'encapsulation de l'objet.

Il est par ailleurs à noter que pour utiliser var person = (Person)factory.GetProxy();

il faut que les propriétés de la classe personne soient marqués virtual, car sinon le proxy ne peut pas les intercepter. On peut aussi utiliser une interface et remplacer la ligne par :

var person = (IPerson)factory.GetProxy();

Dans ce cas, les propriétés n'ont pas besoin d'être marquées virtual. Sur le forum de Spring, Mark Pollack (le co-lead de Spring) m'a expliqué que dans la prochaine version de Spring, il n'y aura plus besoin de marquer les propriétés virtual.

Les Pour :

  • Sont les mêmes que pour Entreprise Library et son Policy Injection Block
  • Le framework spring étant plus puissant, il permet aussi d'injecter l'événement, l'interface et les fonctions

Les Contre :

  • Ici encore on retrouve les mêmes problèmes. L'AOP est un paradigme de programmation auquel il faut s'habituer, et il faut faire l'effort d'apprendre les frameworks. De plus, on crée une dépendance sur notre projet.

La solution .NET 3.5 via Extension methods

Pour finir en beauté, voilà une solution élégante pour l'implémentation de INotifyPropertyChanged. En effet, on peut attacher une méthode d'extension à une interface, ce qui est conceptuellement génial car cela veut dire que l'on peut ajouter un comportement à l'interface quand l'extension est ajoutée ! C'est presque de l'héritage multiple du pauvre et limité, mais ça donne des idées.

public static class NotifyPropertyChangedExtensions
{
    public static void RaisePropertyChanged(
        this INotifyPropertyChanged reference) 
    {
        FieldInfo propertyChangedField = 
            reference.GetType().GetField("PropertyChanged", 
            BindingFlags.Instance | BindingFlags.NonPublic);

        var eventHandler = propertyChangedField.GetValue(reference)
            as PropertyChangedEventHandler;

        if (eventHandler == null) return;

        var previewStackFrame = new StackFrame(1);
        var method = previewStackFrame.GetMethod();
        var propertyName = method.Name.Remove(0, 4);

        eventHandler.Invoke(reference, 
            new PropertyChangedEventArgs(propertyName));
    }
}

Donc comme indiqué, je définis l'extension sur INotifyPropertyChanged : this INotifyPropertyChanged reference

Ensuite, via réflexion, je récupère l'événement comme on l'a vu dans d'autres méthodes. Et pour récupérer le nom de la méthode, j'ai recours à une petite ruse. En effet, grâce à la class StackFrame, je peux remonter la pile des appels et récupérer la méthode appelante. Les setters sont toujours transformés en "set_propertyName", donc il suffit d'enlever le set_ et on a le nom de la propriété.

public string Name { 
    get { return _name; }
    set 
    {
        _name = value;
        this.RaisePropertyChanged();
    }
}

Comme on a affaire à une méthode d'extension, on est obligé de mettre this pour que le compilateur et l'intellisense puissent trouver la méthode.

Optimisation possible

Une optimisation que l'on voit souvent est de ne déclencher l'événement que si la valeur de la propriété a effectivement changé.

if (_name != value)
{
    _name = value;
    this.RaisePropertyChanged();
}

Concrètement, ce genre d'optimisation ne va apporter un plus que si les traitements réalisés en réaction à l'événement sont importants.

Conclusion

Nous avons vu dans ce long post de nombreuses manières d'implémenter INotifyPropertyChanged.

Ce qui est important ici est surtout de voir les différentes approches qui ont été utilisées et qui peuvent être transposées dans d'autres cas. Certaines méthodes visent la maintenabilité, d'autres l'efficacité, d'autres abordent le problème de manière plus générale. Je dirais qu'il n'y a donc pas vraiment de bonne ou de mauvaise méthode : il n'y a que des méthodes différentes qui s'appliquent en fonction des besoins et des contraintes.

La technique .NET 3.5 avec l'extension est sûrement suffisante dans bien des cas. Si on a besoin de plus de performance, alors celle qui met les arguments en singleton static (Version sans création du PropertyChangedEventArgs à chaque fois) est peut être la plus efficace. Si le projet a déjà des dépendances sur Spring ou sur Entreprise Library, il peut être bon de connaître les possibilités et de savoir les utiliser si besoin.

Happy programming !

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 :
Posted: dimanche 15 juin 2008 23:48 par fredhamel

Commentaires

reivilos a dit :

Hello,

concerning

"var propertyName = method.Name.Remove(0, 4);"

how to you know setters always start with "set_" ?

Is there any statically defined constant that says so?

What is someday ..... ?

Have you considered the ActiveSharp option?

http://www.codeplex.com/ActiveSharp

# juin 19, 2008 00:52

fredhamel a dit :

Hello,

Thank you for your comment I removed comment that gives a link to http://www.lhotka.net/ because the link was broken.

Can you please re-post your comment with the working link?

# juin 19, 2008 10:23

fredhamel a dit :

Your point about the setter prefixed by "set_" is very good.

You can find a lots of articles in msdn that talk about it. But I have not find one that state that it will be like that for ever...

To me it's very unlikely to change.

here you can find an article that describe that :

http://msdn.microsoft.com/en-us/magazine/cc301626.aspx

Here is an extract of the article:

"When you create a property, the compiler actually emits special get_PropName and/or set_PropName accessor methods (where PropName is the name of the property). Most compilers will understand these special methods and will allow developers to access the methods using the special property syntax."

Is there any statically defined constant that says so?

I don't know any constant that gives the prefix.

# juin 19, 2008 10:36

reivilos a dit :

It appears the site is undergoing some maintenance right now... the link has worked for me today.

Basically it says that the set method can be inlined

in RELEASE mode and thus the stack trace thing won't (always) work!

See:

http://forums.msdn.microsoft.com/en/csharplanguage/thread/247590ae-4e5a-421d-b7c5-9ec6f8e513d4/

and

http://blogs.msdn.com/ericgu/archive/2004/01/29/64717.aspx

The problem is that as always, we can't force inlining but we can turn it off thanks to

[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]

Here is the transcript from lhotka's site (2006):

--------------------------------------------

nlining issue with PropertyHasChanged, CanReadProperty, CanWriteProperty

The PropertyHasChanged(), CanReadProperty() and CanWriteProperty() methods in CSLA .NET have overloads that allow the developer to call the methods without using a string literal to indicate the property name to which they apply. To accomplish this, the methods use System.Diagnostics to access the stack trace. To avoid the stack from being tampered with by the compiler, these methods are marked with a compiler directive to prevent them from being inlined.

<System.Runtime.CompilerServices.MethodImpl( _="" System.Runtime.CompilerServices.MethodImplOptions.NoInlining)=""> _

   [System.Runtime.CompilerServices.MethodImpl(

     System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]

That is good.

Unfortunately, your business properties can get also be inlined by the compiler, breaking PropertyHasChanged(), CanReadProperty() and CanWriteProperty().

You won’t run into this problem until you build your solution in release mode. In debug mode the inlining won’t occur.

To resolve the issue you have two choices:

  1. Use the string literal forms of the methods. For instance, PropertyHasChanged("MyProperty")

  2. apply the MethodImplOptions.NoInlining attribute to your business properties

Of the two, I would tend to prefer #2 because you preserve the maintainability if your code by avoiding the string literal. However, if you are using code generation to create the bulk of your code, you may opt for #1 because string literals aren’t so bad when created by code gen tools.

--------------------------------------------

# juin 19, 2008 11:12

fredhamel a dit :

Thank you for your input it's realy valuable.

I read that carefully and I try to give an answer later.

I will give also may opignon about ActiveSharp.

# juin 19, 2008 11:36

fredhamel a dit :

Thanks to your comment I will update the post to add the [MethodImpl(MethodImplOptions.NoInlining)] attribute on both the extention and the property.

if you are worried about inlining you can use a modifyed version of the extension like this :

public static void RaisePropertyChanged(

       this INotifyPropertyChanged reference, string propertyName)

   {

       FieldInfo propertyChangedField =

           reference.GetType().GetField("PropertyChanged",

           BindingFlags.Instance | BindingFlags.NonPublic);

       var eventHandler = propertyChangedField.GetValue(reference)

           as PropertyChangedEventHandler;

       if (eventHandler == null) return;

       eventHandler.Invoke(reference,

           new PropertyChangedEventArgs(propertyName));

   }

# juin 19, 2008 17:23

fredhamel a dit :

About ActiveSharp. I was indeed aware of this project but I left it aside on purpose.

1) I dislike adding dependencies to my project to external libraries. They offen do not match our standard of quality. And that means offen debugging and bug fixing unknown code.

However, IMO ActiveSharp seems to be well written and comes with NUnit unittests.

2) The solution use Dictionary lookup, synchronisation lock, heavy reflections, dynamic code Emiting, very low level msil check.

I haven't done some benchmark but my nose tells me that it might be slower than any of the methods shown in this post.

3) IMO the solution is way too clever if a bug is found few peoples will be able to fix the code.

4) IMO the solution is overkill.

5) I dislike having to create a new object in each of my business objet. I would like to have cross-cuting concern off my business objects. That's why I m interested by AOP

Voila :) but if this library meet your needs and you feel that it may be good for you use it.

# juin 19, 2008 18:14

reivilos a dit :

As for the modified version of the extension,

passing the property name as argument is exactly what I would like to avoid (as everybody else I think)!!

All solutions I have come across are either overkill or

inadequate...

I think the StackTrace thing is a good compromise even if it is not based upon specifications.

As for project dependencies.... well, well, well we can't always re-invent the wheel...

I think I might end up passing property names...

Nice article.

# juin 19, 2008 18:45
Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- Merci par Blog de Jérémy Jeanson le 10-01-2019, 20:47

- Office 365: Script PowerShell pour auditer l’usage des Office Groups de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 11:02

- Office 365: Script PowerShell pour auditer l’usage de Microsoft Teams de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 10:39

- Office 365: Script PowerShell pour auditer l’usage de OneDrive for Business de votre tenant par Blog Technique de Romelard Fabrice le 04-25-2019, 15:13

- Office 365: Script PowerShell pour auditer l’usage de SharePoint Online de votre tenant par Blog Technique de Romelard Fabrice le 02-27-2019, 13:39

- Office 365: Script PowerShell pour auditer l’usage d’Exchange Online de votre tenant par Blog Technique de Romelard Fabrice le 02-25-2019, 15:07

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Stream Portal par Blog Technique de Romelard Fabrice le 02-21-2019, 17:56

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Video Portal par Blog Technique de Romelard Fabrice le 02-18-2019, 18:56

- Office 365: Script PowerShell pour extraire les Audit Log basés sur des filtres fournis par Blog Technique de Romelard Fabrice le 01-28-2019, 16:13

- SharePoint Online: Script PowerShell pour désactiver l’Option IRM des sites SPO non autorisés par Blog Technique de Romelard Fabrice le 12-14-2018, 13:01