【问题标题】:How to intercept only base class methods with Castle.DynamicProxy如何使用 Castle.DynamicProxy 仅拦截基类方法
【发布时间】:2018-07-04 23:29:49
【问题描述】:

我有 2 个类,一个用于访问数据库和具有缓存的子类。我可以更改这两个类的源代码,但是有许多具有不同结构的类,所以我正在寻找一种方法来制作通用解决方案,以帮助我只拦截我用Attribute 或其他方式标记的方法。

这是一个例子

public class BaseClass
{
    [MyAttribute]
    public virtual MyEntity[] GetAll() {}
    [MyAttribute]
    public virtual MyEntity GetByKey(int key) {}
    [MyAttribute]
    public virtual void GetByName(string name) {}
}

public class ChildClass : BaseClass
{
    public override MyEntity GetByKey(int key) 
    {
        if(key > 100)
           return GetCachedEntity(key);
        return base.GetByKey(key);
    }
}

public class MyInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        // Here I can check any required conditions to decide on the way of processing
        var myCondition = invocation.Method.GetCustomAttributes(false).Any(a => a is MyAttribute);
        if(myCondition)
        {
            // extra logic for marked methods
        }

        invocation.Proceed();
    }
}

public static class MyProxyFactory
{
    private static readonly ProxyGenerator proxyGenerator = new ProxyGenerator();

    // here is my generic proxy factory which I want to use for creating proxies for ChildClass objects and another objects that contains similar logic
    public static TInterface CreateProxy<TInterface>(TInterface concreteObject)
        where TInterface : class
    {
        var proxy = proxyGenerator.CreateInterfaceProxyWithTarget(concreteObject, ProxyGenerationOptions.Default, new MyInterceptor());
        return proxy;
    }
}

[AttributeUsage(AttributeTargets.Method, Inherited = false)]
public class MyAttribute : Attribute {}

我正在尝试将invocation.Method.GetCustomAttributes() 用于myCondition 并仅标记基类方法,但问题是当ChildClass.GetByKey() 调用基类方法时,它不会使用MyInterceptor.Intercept() 方法进行拦截。

对于这个例子,我可以用分解替换继承,但是我需要在ChildClass 中实现GetAllGetByName 方法,这个解决方案将不是通用的。

如何更改ProxyGenerator 设置或CreateProxy() 方法来解决我的问题?

var realObject = new ChildClass();
var proxyObject = MyProxyFactory.CreateProxy(realObject);

// extra logic should be executed
proxyObject.GetAll();

// extra logic should be executed
proxyObject.GetByKey(99);

// extra logic should not be executed
proxyObject.GetByKey(101);

【问题讨论】:

  • 也许你应该添加一些代码来测试和证明你想说的话
  • @NSGaga 我在问题末尾添加了代码。您可以假设 MyInterceptor 中的额外逻辑包含 Console.WriteLine("query to database was executed") 然后 proxyObject.GetByKey(99); 应该在控制台上写入此消息而 proxyObject.GetByKey(101); 不应该。

标签: c# system.reflection castle-dynamicproxy


【解决方案1】:

base.Method 调用故意不尝试查找比this 的基类更远的继承关系的任何方法重载。代理不会执行花哨的魔法,它会覆盖子类中的方法并依赖虚拟调用来访问代理而不是其基本实现。或者如果它是一个包装代理,它只拦截外部调用 - 我不知道你使用的代理类型的详细信息。

所以基本上,一旦你写了base.Method,你就不能指望任何子类魔法发生。如果您需要在层次结构内部的子父交互中进行拦截,则需要重新访问您的设计方法。

【讨论】:

  • 我可以描述可以解决我的问题的解决方案,我只是希望Castle.DynamicProxy 已经通过设置提供了现成的解决方案。在创建代理时,ProxyGenerator 可以迭代继承层次结构并为层次结构中的每个类创建代理。如果我们使用class B : a 的假想ProxyGenerationOptions.ProxyAll 参数创建代理,最终层次结构将是A -&gt; ProxyA -&gt; B -&gt; ProxyB。如果我们在B 对象中调用base.Foo(),我们将调用ProxyA.Foo() 方法并进行拦截。
  • 我发现ProxyGenerator 没有密封然后我可以基于它创建自己的生成器并在现有方法上实现所需的逻辑。
  • @VadimMartynov 这很有趣...我不知道您实际上可以重写一个类,使其祖先层次结构发生变化(从A -&gt; BA -&gt; Proxy -&gt; B)。老实说,我不知道如何在没有预编译代码重写器的情况下实现这个结果,所以也许我们会在这个问题上得到另一个答案。
  • @VadimMartynov 根据请求动态制作新类型是一回事。动态更改已经存在的类型(例如 B)是完全不同的事情。为此,您必须修改 CLR,所以我很确定这种方法行不通。只有一种方法可以更改 CLR 中的现有类型,即卸载其应用程序域并使用该类型的修改版本创建一个新的应用程序域。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多