Skip to content

Not Intercepted Method is invoked on target, not on proxy. #647

@joelweiss

Description

@joelweiss

This is a simplified version of what I am doing, but this should be enough to explain.

I want to add INotifyPropertyChanged to a POCO (Blog) class,
and I want to raise PropertyChanged even if the property is changed by a method on the Target (SetBlogId()), this no longer works after 5.0.0 probably because #571 changed the SetBlogId() execution to be on the target not on the proxy.

Is there any way I can invoke the target.Method() on the Proxy?

in 4.4.1 the output is
Change event was raised as expected
in 5.0.0 the output is
Expected a change event

using System;
using Castle.DynamicProxy;
using System.ComponentModel;
using System.Reflection;

internal class Program
{
    private static void Main(string[] args)
    {
        Blog blog = new Blog();
        ProxyGenerator generator = new ProxyGenerator();
        Blog proxy = (Blog)generator.CreateClassProxyWithTarget(
            classToProxy: typeof(Blog),
            additionalInterfacesToProxy: new[] { typeof(INotifyPropertyChanged) },
            options: new ProxyGenerationOptions(new PropertiesOnlyGenerationHook()),
            target: blog,
            interceptors: new INotifyPropertyChangedInterceptor());
        bool propertyChangedEventRaised = false;

        ((INotifyPropertyChanged)proxy).PropertyChanged += (o, e) => propertyChangedEventRaised = true;

        proxy.SetBlogId(1000);

        Console.WriteLine(propertyChangedEventRaised ? "Change event was raised as expected" : "Expected a change event");

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

internal class PropertiesOnlyGenerationHook : IProxyGenerationHook
{
    public void MethodsInspected() { }
    public void NonProxyableMemberNotification(Type type, MemberInfo memberInfo) { }
    // intercept propery and event setters
    public bool ShouldInterceptMethod(Type type, MethodInfo methodInfo) => methodInfo.IsSpecialName;
}

internal class INotifyPropertyChangedInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        if (invocation.Method.Name.StartsWith("set_", StringComparison.Ordinal))
        {
            string propertyName = invocation.Method.Name["set_".Length..];
            invocation.Proceed();
            PropertyChanged?.Invoke(invocation.Proxy, new PropertyChangedEventArgs(propertyName));
        }
        else if (invocation.Method.Name == "add_PropertyChanged")
        {
            PropertyChanged += (PropertyChangedEventHandler)invocation.Arguments[0];
        }
        else if (invocation.Method.Name == "remove_PropertyChanged")
        {
            PropertyChanged -= (PropertyChangedEventHandler)invocation.Arguments[0];
        }
        else
        {
            invocation.Proceed();
        }
    }

    private event PropertyChangedEventHandler? PropertyChanged;
}

public class Blog
{
    public virtual int BlogId { get; set; }
    public virtual void SetBlogId(int blogId) => BlogId = blogId;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions