如何在运行时配置Unity 2.0以拦截INotifyPropertyChanged?

时间:2010-11-29 10:59:50

标签: c# unity-container aop inotifypropertychanged

http://msdn.microsoft.com/en-us/library/ff660851(v=PandP.20).aspx提供了如何实现IInterceptionBehavior以添加INotifyPropertyChanged支持的示例。示例不包括如何配置要在运行时使用的NotifyPropertyChangedBehavior。我所做的所有谷歌搜索都没有给我一个合适的答案。

我是AOP和IoC的新手,所以也许我的概念也错了。这就是我想要做的事情:

using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.InterceptionExtension;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;

static class AppMain
{
    public static void Main()
    {
        // Configure Unity.
        var container = new UnityContainer();

        container.AddNewExtension<Interception>();

        // todo: Register an interface instead of a type.
        container.RegisterType<Customer>(new Interceptor<VirtualMethodInterceptor>(), 
                                         new InterceptionBehavior<NotifyPropertyChangedBehavior>());

        var propertyChangedCount = 0;
        var customer = new Customer();

        customer.PropertyChanged += (s, e) => propertyChangedCount += 1;

        // Update customer and send property changed event.
        customer.FirstName = "what ever";

        if (propertyChangedCount != 1)
        {
            Console.Write("Failed!");
        }
        else
        {
            Console.WriteLine("Success!");
        }

        Console.WriteLine();
        Console.WriteLine("Press any key to continue...");
        Console.ReadKey();

    }

    static void customer_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        throw new NotImplementedException();
    }

    public class Customer : MarshalByRefObject, INotifyPropertyChanged
    {

        private string _firstName;
        public event PropertyChangedEventHandler PropertyChanged;

        // todo: Does the property have to be virtual (overridable).
        public virtual string FirstName
        {
            get { return _firstName; }
            set
            {
                _firstName = value;
                // Unity Interception to do the following RaiseEvent
                //if (PropertyChanged != null)
                //{
                //    PropertyChanged(this, new PropertyChangedEventArgs("FirstName"));
                //}
            }
        }

    }

    // Copied from http://msdn.microsoft.com/en-us/library/ff660851(v=PandP.20).aspx
    class NotifyPropertyChangedBehavior : IInterceptionBehavior
    {
        private event PropertyChangedEventHandler propertyChanged;

        private static readonly MethodInfo addEventMethodInfo =
            typeof(INotifyPropertyChanged).GetEvent("PropertyChanged").GetAddMethod();

        private static readonly MethodInfo removeEventMethodInfo =
            typeof(INotifyPropertyChanged).GetEvent("PropertyChanged").GetRemoveMethod();

        public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
        {
            if (input.MethodBase == addEventMethodInfo)
            {
                return AddEventSubscription(input, getNext);
            }
            if (input.MethodBase == removeEventMethodInfo)
            {
                return RemoveEventSubscription(input, getNext);
            }
            if (IsPropertySetter(input))
            {
                return InterceptPropertySet(input, getNext);
            }
            return getNext()(input, getNext);
        }

        public bool WillExecute
        {
            get { return true; }
        }

        public IEnumerable<Type> GetRequiredInterfaces()
        {
            return new[] { typeof(INotifyPropertyChanged) };
        }

        private IMethodReturn AddEventSubscription(IMethodInvocation input, 
                                                   GetNextInterceptionBehaviorDelegate getNext)
        {
            var subscriber = (PropertyChangedEventHandler)input.Arguments[0];

            propertyChanged += subscriber;
            return input.CreateMethodReturn(null);
        }

        private IMethodReturn RemoveEventSubscription(IMethodInvocation input, 
                                                      GetNextInterceptionBehaviorDelegate getNext)
        {
            var subscriber = (PropertyChangedEventHandler)input.Arguments[0];

            propertyChanged -= subscriber;
            return input.CreateMethodReturn(null);
        }

        private static bool IsPropertySetter(IMethodInvocation input)
        {
            return input.MethodBase.IsSpecialName && input.MethodBase.Name.StartsWith("set_");
        }

        private IMethodReturn InterceptPropertySet(IMethodInvocation input, 
                                                   GetNextInterceptionBehaviorDelegate getNext)
        {
            var propertyName = input.MethodBase.Name.Substring(4);
            var returnValue = getNext()(input, getNext);
            var subscribers = propertyChanged;

            if (subscribers != null)
            {
                subscribers(input.Target, new PropertyChangedEventArgs(propertyName));
            }

            return returnValue;
        }
    }
}

3 个答案:

答案 0 :(得分:2)

一些IL编织不会让这更容易吗?

NotifyPropertyWeaverhttp://www.sharpcrafters.com/

答案 1 :(得分:1)

herehere。基本上,你必须注册类型和拦截器:

Dim container As IUnityContainer = New UnityContainer()
   container.AddNewExtension(Of Interception)()
   container.RegisterType(Of Customer)( _
          New Interceptor(Of VirtualMethodInterceptor)(), _
          New InterceptionBehavior(Of NotifyPropertyChangedBehavior)())

答案 2 :(得分:0)

我需要使用VirtualMethodInterceptor。我发现NotifyPropertyChangedBehavior.Invoke检查PropertyChanged添加或删除从来都不是真的。我改为检查方法名称匹配和事情是否有效。

原始NotifyPropertyChangedBehavior来自msdn上的Unity文档。我很感兴趣,如果有人能告诉我为什么原始代码不起作用。

班级定义

public class NotifyPropertyChangeClass 
    : INotifyPropertyChanged
{
    public virtual int SomeInt { get; set; }

    public virtual event PropertyChangedEventHandler PropertyChanged;
}

IUnityContainer设置

container.AddNewExtension<Interception>();
container.RegisterType<NotifyPropertyChangeClass>(
    new Interceptor<VirtualMethodInterceptor>(),
    new InterceptionBehavior(new NotifyPropertyChangedBehavior()));

NotifyPropertyChangedBehavior修改(original

public class NotifyPropertyChangedBehavior : IInterceptionBehavior
{
...
    public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
    {
        if (input.MethodBase.Name.Equals(addEventMethodInfo.Name))//(input.MethodBase == addEventMethodInfo)
        {
            return AddEventSubscription(input, getNext);
        }
        if (input.MethodBase.Name.Equals(removeEventMethodInfo.Name))//(input.MethodBase == removeEventMethodInfo)
        {
            return RemoveEventSubscription(input, getNext);
        }
        if (IsPropertySetter(input))
        {
            return InterceptPropertySet(input, getNext);
        }
        return getNext()(input, getNext);
    }
...
}