获取成员拦截的属性值

Ray*_*und 2 c# castle code-injection autofac interception

我有这个CacheAttribute接受这样的持续时间值

public class MyTestQuery : IMyTestQuery
{
    private readonly ISomeRepository _someRepository;

    public TestQuery(ISomeRepository someRepository)
    {
        _someRepository = someRepository;
    }

    [Cache(Duration = 10)]
    public MyViewModel GetForeignKeysViewModelCache()
    {
        ...code here...
        return viewModel;
    }
}
Run Code Online (Sandbox Code Playgroud)

属性看起来像这样

[AttributeUsage(AttributeTargets.Method)]
public class CacheAttribute : Attribute
{
    public int Duration { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

当使用Castle.Proxy.IInterceptor拦截时,它可以工作,但是当我通过IInitation.MethodInitationTarget或IInitation.Method执行Attribute.GetCustomAttribute时,两者都会返回null值

这是代码中的

public class CacheResultInterceptor : IInterceptor
{
    public CacheAttribute GetCacheResultAttribute(IInvocation invocation)
    {
        var methodInfo = invocation.MethodInvocationTarget;
        if (methodInfo == null)
        {
            methodInfo = invocation.Method;
        }

        return Attribute.GetCustomAttribute(
            methodInfo,
            typeof(CacheAttribute),
            true
        )
        as CacheAttribute;
    }
    public void Intercept(IInvocation invocation)
    {
        var cacheAttribute = GetCacheResultAttribute(invocation);
        //cacheAttribute is null always

        ...more code here...
    }
}
Run Code Online (Sandbox Code Playgroud)

这就是我注册它们的方式

public class Bootstrapper
{
    public static ContainerBuilder Builder;

    public static void Initialise()
    {
        Builder = new ContainerBuilder();

        ...other codes in here...
        CacheInstaller.Install();

        var container = Builder.Build();
        DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
    }

}

public class CacheInstaller 
{
    public static void Install()
    {

        Bootstrapper.Builder.RegisterType<CacheResultInterceptor>()
            .SingleInstance();

        Bootstrapper.Builder.RegisterAssemblyTypes(Assembly.Load("MyApplication.Web"))
            .Where(t => t.Name.EndsWith("Query"))
            .AsImplementedInterfaces()
            .EnableInterfaceInterceptors()
            .InterceptedBy(typeof(CacheResultInterceptor))
            .SingleInstance();

    }
}
Run Code Online (Sandbox Code Playgroud)

我的昂贵方法类以查询结束

现在的问题是为什么invocation.MethodInvocationTargetand/orinvocation.Method返回 null?我究竟做错了什么?还有其他策略可以让我传递参数值而无需为我能想到的每个值创建方法吗?

顺便说一句我正在使用

  • Autofac 4.3.0.0
  • Autofac.Extras.DynamicProxy 4.2.1.0
  • Autofac.Integration.Mvc 4.0.0.0
  • 城堡核心 4.0.0.0

更新 1 为了清楚起见,这是它运行时返回的内容

在此输入图像描述

Mic*_*iey 5

这是我发现的。

\n\n

invocation.Method在您的情况下,返回接口上的方法声明IMyTestQuery。

\n\n

另一方面,invocation.MethodInvocationProxy返回调用时将要调用的方法invocation.Proceed()。这意味着它可以是:

\n\n
    \n
  • 下一个拦截器(如果有多个)
  • \n
  • 装饰器(如果您的界面上有装饰器)
  • \n
  • 决赛​接口的
  • \n
\n\n

正如您所看到的,MethodInvocationProxy的确定性不如Method,这就是为什么我建议您避免使用它,至少对于您想要实现的目标而言。

\n\n

当您考虑一下时,拦截器不应该与实现绑定,因为它代理接口,那么为什么不将该[Cache]属性放在接口级别呢?

\n\n

使用您的代码,我可以在将其放在界面上时成功检索它。

\n\n
\n\n

编辑:

\n\n

好的,我已经在 GitHub 上构建了一个存储库,该存储库使用您提到的 NuGet 包的特定版本,并展示了如何检索被拦截方法的属性。

\n\n

提醒一下,以下是使用的 NuGet 包:

\n\n
    \n
  • Microsoft.AspNet.Mvcv5.2.3
  • \n
  • Autofacv4.3.0
  • \n
  • Autofac.Mvc54.0.0
  • \n
  • Autofac.Extras.DynamicProxyv4.2.1
  • \n
  • Castle.Corev4.0.0
  • \n
\n\n

我创建了 2 个查询接口,IMyQuery并且IMySecondQuery. 请注意,正如我原来的答案中提到的,[Cache]属性放置在接口方法上,而不是放置在实现类上。

\n\n
public interface IMyQuery\n{\n    [Cache(60000)]\n    string GetName();\n}\n\npublic interface IMySecondQuery\n{\n    [Cache(1000)]\n    string GetSecondName();\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后我们有这些类的两个非常基本的实现。完全不相关,但为了完整性:

\n\n
public class DefaultMyQuery : IMyQuery\n{\n    public string GetName()\n    {\n        return "Raymund";\n    }\n}\n\npublic class DefaultMySecondQuery : IMySecondQuery\n{\n    public string GetSecondName()\n    {\n        return "Micka\xc3\xabl Derriey";\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后是拦截器:

\n\n
public class CacheResultInterceptor : IInterceptor\n{\n    public void Intercept(IInvocation invocation)\n    {\n        var cacheAttribute = invocation.Method.GetCustomAttribute<CacheAttribute>();\n        if (cacheAttribute != null)\n        {\n            Trace.WriteLine($"Found a [Cache] attribute on the {invocation.Method.Name} method with a duration of {cacheAttribute.Duration}.");\n        }\n\n        invocation.Proceed();\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

请注意,该方法是命名空间中GetCustomAttribute<T>存在的扩展方法。MemberInfoSystem.Reflection

\n\n

让我们继续在 Autofac 容器中注册。我尽力遵循您的注册风格:

\n\n
public class CacheResultInterceptor : IInterceptor\n{\n    public void Intercept(IInvocation invocation)\n    {\n        var cacheAttribute = invocation.Method.GetCustomAttribute<CacheAttribute>();\n        if (cacheAttribute != null)\n        {\n            Trace.WriteLine($"Found a [Cache] attribute on the {invocation.Method.Name} method with a duration of {cacheAttribute.Duration}.");\n        }\n\n        invocation.Proceed();\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后在以下位置使用查询HomeController:

\n\n
var builder = new ContainerBuilder();\n\nbuilder.RegisterControllers(typeof(MvcApplication).Assembly);\n\nbuilder\n    .RegisterType<CacheResultInterceptor>()\n    .SingleInstance();\n\nbuilder\n    .RegisterAssemblyTypes(typeof(MvcApplication).Assembly)\n    .Where(x => x.Name.EndsWith("Query"))\n    .AsImplementedInterfaces()\n    .EnableInterfaceInterceptors()\n    .InterceptedBy(typeof(CacheResultInterceptor));\n\nDependencyResolver.SetResolver(new AutofacDependencyResolver(builder.Build()));\n
Run Code Online (Sandbox Code Playgroud)\n\n

我所做的测试只是在拦截器、F5应用程序中放置一个断点,打开浏览器并导航到http://localhost:62440/home/myquery和http://localhost:62440/home/myquery。

\n\n

它确实击中了拦截器并找到了[Cache]属性。在 Visual Studio 输出窗口中,它确实显示:

\n\n
Found a [Cache] attribute on the GetName method with a duration of 60000.\nFound a [Cache] attribute on the GetSecondName method with a duration of 1000.\n
Run Code Online (Sandbox Code Playgroud)\n\n

希望这可以帮助您查明项目中发生的情况。

\n\n
\n\n

我将更改推送到存储库,以便第一个查询调用第二个查询。

\n\n

它仍然有效。您确实应该付出努力并在问题上添加一些代码。

\n