C# 通过castle动态代理使用非空构造函数代理Ninject拦截类

C# 通过castle动态代理使用非空构造函数代理Ninject拦截类,c#,ninject,aop,castle-dynamicproxy,ninject-interception,C#,Ninject,Aop,Castle Dynamicproxy,Ninject Interception,我目前的大部分实施基于此处提供的信息: 我使用一个定制的规划策略类,它查找具有给定属性(而不是ninject拦截器属性)的所有方法,如果这些属性与标准匹配,那么这些方法将被代理 使用的一个例子是: Kernel.Components.Add() 然后,这将查找具有[Logging]属性的任何方法,然后将使用日志拦截器 但是,当动态代理试图代理启用相关属性的方法时,我当前从动态代理中获得InvalidProxyConstructorArgumentsException。现在我记得读到过需要虚拟方

我目前的大部分实施基于此处提供的信息:

我使用一个定制的规划策略类,它查找具有给定属性(而不是ninject拦截器属性)的所有方法,如果这些属性与标准匹配,那么这些方法将被代理

使用的一个例子是:

Kernel.Components.Add()

然后,这将查找具有
[Logging]
属性的任何方法,然后将使用日志拦截器

但是,当动态代理试图代理启用相关属性的方法时,我当前从动态代理中获得InvalidProxyConstructorArgumentsException。现在我记得读到过需要虚拟方法的文章,但是我不记得看到必须有一个无参数构造函数

所有绑定都是针对接口进行的,AOP拦截器是通过上面链接中提到的属性和自定义代理规划类进行的


那么,有没有一种方法可以让动态代理(或linfu版本)代理具有依赖构造函数的类呢?(所有依赖项都在内核中,因此它们不是无法解析的)。

查看代理生成代码:

并具有以下约束力:

Bind<Foo>().ToSelf().Intercept().With<SomeInterceptor>();
Bind<IBar>().To<Bar>();
不行

将所有构造函数参数放在ninject上下文中以使其工作

但是,我们可以更改
Foo
的检索以使其工作:

var bar = IResolutionRoot.Get<IBar>();
IResolutionRoot.Get<Foo>(new ConstructorArgument("bar", bar);
然后我们将绑定更改为:

Bind<IFoo>().To<Foo>().Intercept().With<SomeInterceptor>();
工作

另一种可能更简单(更丑)的解决方案 根据@Daniel的说法,这项工作: 向代理类型添加两个构造函数:

  • 一个受保护的
构造函数,不带参数。这一个用于DynamicProxy创建代理
  • 一个带有参数的
    公共
    /
    内部
    构造函数,供ninject用于实例化代理类型

  • Ninject将自动选择具有最多可解析参数的构造函数。

    另一种方法是使用带有
    [Logging]
    属性的方法对所有类使用基于约定的绑定。但是,这意味着向方法添加
    [Logging]
    属性将影响对象的绑定,这可能是不需要的

    这就是它的工作原理(已验证有效):


    请具体说明。你的绑定是什么?您的拦截器实现是什么?您使用的是自定义
    IPlanningStrategy
    还是简单的
    .Intercept()。使用()
    ?上面的链接详细介绍了它,我使用属性(不是从
    InterceptorAttribute
    继承的)然后使用自定义计划策略类遍历类,检查具有属性的方法,然后代理它。将用一个示例更新主要问题。啊,好吧,如果有任何构造函数具有不明确的依赖项,它将不知道该做什么,并会崩溃。可能我在挑剔/可能我没有正确理解您:代理类所采用的每个构造函数参数必须在ninject上下文中作为类型的参数可用
    ConstructorArgument
    。代理类没有依赖项解析。除非您使用“带目标代理的接口”:否则,该类将“像往常一样”使用依赖项解析进行实例化,并且只有接口代理在不使用依赖项解析的情况下被实例化-它无论如何都不需要它。只是为了澄清,所有内容都经过Ninject,因此所有类和构造函数参数都在依赖项树中。构造函数参数(“someArg”,someObj)没有手动
    ,因为树中的所有内容都会被填充,并且类本身工作正常,不涉及AOP。就在我尝试连接上面显示的属性截取的那一刻,它不能代理类,我假设在考虑了您的答案之后,可能是因为某些构造函数可能有一个模棱两可的参数,与共享同一接口但有两个不同实现的参数类似。AFAIK:如果没有
    。WithConstructorArgument(..)
    但代理需要构造函数参数,则代理将失败。因为ninject不为“没有目标的类代理”的代理类执行构造函数注入。即使依赖关系是明确的,它也会失败。可以相对容易地做到的是,使用约定扩展找到所有具有1+方法的类,这些方法具有
    [Logging]
    属性,然后将它们绑定到它们的接口和“拦截”。这似乎是一种解决问题的有趣方法,将此标记为答案,因为这为我们提供了所需的分离,还允许我们为每个属性配置拦截器。谢谢你花时间教育我和其他人:)
    IResolutionRoot.Get<Foo>();
    
    var bar = IResolutionRoot.Get<IBar>();
    IResolutionRoot.Get<Foo>(new ConstructorArgument("bar", bar);
    
    public interface IFoo{ }
    
    public class Foo : IFoo
    {
         public Foo(IBar bar)
         {
         }
    }
    
    Bind<IFoo>().To<Foo>().Intercept().With<SomeInterceptor>();
    
    IResolutionRoot.Get<Foo>();
    
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
    public sealed class LoggingAttribute : Attribute
    {
    }
    
    public interface IClassNotToBeIntercepted
    {
        void DoSomething();
    }
    
    public class ClassNotToBeIntercepted : IClassNotToBeIntercepted
    {
        public void DoSomething() { }
    }
    
    public interface IClassToBeIntercepted
    {
        void DoNotLogThis();
        void LogThis();
        void LogThisAsWell();
    }
    
    public class ClassToBeIntercepted : IClassToBeIntercepted
    {
        public void DoNotLogThis() { }
    
        [Logging]
        public void LogThis() { }
    
        [Logging]
        public void LogThisAsWell() { }
    }
    
    public class LoggingInterceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            Console.WriteLine("interceptor before {0}", BuildLogName(invocation));
    
            invocation.Proceed();
    
            Console.WriteLine("interceptor after {0}", BuildLogName(invocation));
        }
    
        private static string BuildLogName(IInvocation invocation)
        {
            return string.Format(
                "{0}.{1}", 
                invocation.Request.Target.GetType().Name,
                invocation.Request.Method.Name);
        }
    }
    
    public class DemoModule : NinjectModule
    {
        public override void Load()
        {
            this.Bind(convention => convention
                .FromThisAssembly()
                .SelectAllClasses()
                .Where(ContainsMethodWithLoggingAttribute)
                .BindDefaultInterface()
                .Configure(x => x
                    .Intercept()
                    .With<LoggingInterceptor>()));
    
            this.Bind<IClassNotToBeIntercepted>()
                .To<ClassNotToBeIntercepted>();
        }
    
        private static bool ContainsMethodWithLoggingAttribute(Type type)
        {
            return type
                .GetMethods()
                .Any(method => method.HasAttribute<LoggingAttribute>());
        }
    }
    
        [Fact]
        public void InterceptorTest()
        {
            var kernel = new StandardKernel();
            kernel.Load<DemoModule>();
    
            kernel.Get<IClassNotToBeIntercepted>()
                .DoSomething();
    
            kernel.Get<IClassToBeIntercepted>()
                .LogThis();
        }
    
    interceptor before ClassToBeIntercepted.LogThis
    interceptor after ClassToBeIntercepted.LogThis