How to configure Unity 2.0 at runtime to intercept INotifyPropertyChanged?
http://msdn.microsoft.com/en-us/library/ff660851(v=PandP.20).aspx provides a sample of how to implement IInterceptionBehavior to add INotifyPropertyChanged support. What the sample does not include is how to configure NotifyPropertyChangedBehavior to be used at runtime. All the googling I've done has not given me a working answer.
I'm new to AOP and IoC so maybe I've got the concept wrong as well. This is what I want to do:
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;
retur开发者_如何转开发n 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;
}
}
}
Tim
Wouldn't some IL weaving make this easier?
NotifyPropertyWeaver Or http://www.sharpcrafters.com/ ?
It's here and here. Basically, you have to register the type and the interceptor:
Dim container As IUnityContainer = New UnityContainer()
container.AddNewExtension(Of Interception)()
container.RegisterType(Of Customer)( _
New Interceptor(Of VirtualMethodInterceptor)(), _
New InterceptionBehavior(Of NotifyPropertyChangedBehavior)())
I need to use the VirtualMethodInterceptor. I found the NotifyPropertyChangedBehavior.Invoke check for a PropertyChanged add or remove was never true. I changed to check for a method name match and things work.
The original NotifyPropertyChangedBehavior is from Unity documentation on msdn. I am interested if anyone can tell me why the original code doesn't work.
Class Definition
public class NotifyPropertyChangeClass
: INotifyPropertyChanged
{
public virtual int SomeInt { get; set; }
public virtual event PropertyChangedEventHandler PropertyChanged;
}
IUnityContainer Setup
container.AddNewExtension<Interception>();
container.RegisterType<NotifyPropertyChangeClass>(
new Interceptor<VirtualMethodInterceptor>(),
new InterceptionBehavior(new NotifyPropertyChangedBehavior()));
NotifyPropertyChangedBehavior modification (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);
}
...
}
精彩评论