获取成员拦截的属性值

时间:2017-02-27 18:40:50

标签: c# autofac code-injection castle 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;
    }
}

属性看起来像这样

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

当使用 Castle.Proxy.IInterceptor 截获时,它可以正常工作,但当我通过 IInvocation.MethodInvocationTarget Attribute.GetCustomAttribute 时> IInvocation.Method 都返回 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...
    }
}

这就是我注册的方式

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();

    }
}

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

现在问题是为什么invocation.MethodInvocationTarget和/或invocation.Method返回null? 我究竟做错了什么? 任何其他策略,所以我可以传递参数值,而不为我能想到的每个值创建一个方法?

BTW我正在使用

  • Autofac 4.3.0.0
  • Autofac.Extras.DynamicProxy 4.2.1.0
  • Autofac.Integration.Mvc 4.0.0.0
  • Castle.Core 4.0.0.0

更新1 以下是为了清晰起见而返回的内容

enter image description here

1 个答案:

答案 0 :(得分:3)

这是我找到的。

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

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

  • 下一个拦截器,如果你有几个
  • 如果您的界面上有装饰器,则为装饰者
  • 接口的最终实施

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

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

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

修改

好的,我已经整理了一个repository on GitHub,它使用了你提到的NuGet包的特定版本,并展示了如何检索截获方法的属性。

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

  • Microsoft.AspNet.Mvc v5.2.3
  • Autofac v4.3.0
  • Autofac.Mvc5 4.0.0
  • Autofac.Extras.DynamicProxy v4.2.1
  • Castle.Core v4.0.0

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

public interface IMyQuery
{
    [Cache(60000)]
    string GetName();
}

public interface IMySecondQuery
{
    [Cache(1000)]
    string GetSecondName();
}

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

public class DefaultMyQuery : IMyQuery
{
    public string GetName()
    {
        return "Raymund";
    }
}

public class DefaultMySecondQuery : IMySecondQuery
{
    public string GetSecondName()
    {
        return "Mickaël Derriey";
    }
}

然后是拦截器:

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

        invocation.Proceed();
    }
}

请注意,GetCustomAttribute<T>方法是MemberInfo命名空间中System.Reflection的扩展方法。

让我们继续进行Autofac容器中的注册。我试着尽可能地遵循你的注册风格:

var builder = new ContainerBuilder();

builder.RegisterControllers(typeof(MvcApplication).Assembly);

builder
    .RegisterType<CacheResultInterceptor>()
    .SingleInstance();

builder
    .RegisterAssemblyTypes(typeof(MvcApplication).Assembly)
    .Where(x => x.Name.EndsWith("Query"))
    .AsImplementedInterfaces()
    .EnableInterfaceInterceptors()
    .InterceptedBy(typeof(CacheResultInterceptor));

DependencyResolver.SetResolver(new AutofacDependencyResolver(builder.Build()));

然后在HomeController

中使用查询
public class HomeController : Controller
{
    private readonly IMyQuery _myQuery;
    private readonly IMySecondQuery _mySecondQuery;

    public HomeController(IMyQuery myQuery, IMySecondQuery mySecondQuery)
    {
        _myQuery = myQuery;
        _mySecondQuery = mySecondQuery;
    }

    public ActionResult MyQuery()
    {
        return Json(_myQuery.GetName(), JsonRequestBehavior.AllowGet);
    }

    public ActionResult MySecondQuery()
    {
        return Json(_mySecondQuery.GetSecondName(), JsonRequestBehavior.AllowGet);
    }
}

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

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

Found a [Cache] attribute on the GetName method with a duration of 60000.
Found a [Cache] attribute on the GetSecondName method with a duration of 1000.

希望这可以帮助您确定项目中发生的事情。

pushed changes to the repository,以便第一个查询调用第二个查询。

它仍然有效。你应该真的付出努力并在问题上加上一些代码。