获取成员拦截的属性值
Getting Attribute Value on Member Interception
我有这个 CacheAttribute 接受 Duration 这样的值
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 拦截时它有效但是当我通过 [=38 执行 Attribute.GetCustomAttribute =]IInvocation.MethodInvocationTarget or IInvocation.Method both returns a null value
这是代码
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();
}
}
我的昂贵方法Class以查询结束
现在的问题是为什么invocation.MethodInvocationTarget
and/orinvocation.Method
returns为null?
我究竟做错了什么?
任何其他策略,以便我可以传递参数值而无需为我能想到的每个值创建方法?
顺便说一句,我正在使用
- 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
为清楚起见,下面是它 returns 运行时的内容
这是我的发现。
invocation.Method
returns 接口上的方法声明,在你的例子中 IMyTestQuery
.
另一方面,invocation.MethodInvocationProxy
returns 将在调用 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 个 query 接口,IMyQuery
和 IMySecondQuery
。请注意,正如我最初的回答中提到的,[Cache]
属性位于接口方法上,而不是实现 类.
public interface IMyQuery
{
[Cache(60000)]
string GetName();
}
public interface IMySecondQuery
{
[Cache(1000)]
string GetSecondName();
}
然后我们有 2 个非常基本的实现 类。完全不相关,但为了完整起见:
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>
方法是 System.Reflection
命名空间中 MemberInfo
的扩展方法。
让我们继续在 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/myquery
和 http://localhost:62440/home/myquery
。
确实命中了拦截器,找到了[Cache]
属性。在 Visual Studio 输出 window 中,确实显示:
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 以便第一个查询调用第二个。
它仍然有效。你真的应该努力把一些代码放在这个问题上。
我有这个 CacheAttribute 接受 Duration 这样的值
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 拦截时它有效但是当我通过 [=38 执行 Attribute.GetCustomAttribute =]IInvocation.MethodInvocationTarget or IInvocation.Method both returns a null value
这是代码
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();
}
}
我的昂贵方法Class以查询结束
现在的问题是为什么invocation.MethodInvocationTarget
and/orinvocation.Method
returns为null?
我究竟做错了什么?
任何其他策略,以便我可以传递参数值而无需为我能想到的每个值创建方法?
顺便说一句,我正在使用
- 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 为清楚起见,下面是它 returns 运行时的内容
这是我的发现。
invocation.Method
returns 接口上的方法声明,在你的例子中 IMyTestQuery
.
另一方面,invocation.MethodInvocationProxy
returns 将在调用 invocation.Proceed()
时调用的方法。这意味着它可以是:
- 下一个拦截器如果你有几个
- 一个装饰器,如果你的界面上有装饰器
- 你的接口的最终实现
如您所见,MethodInvocationProxy
的确定性不如 Method
,这就是为什么我建议您避免使用它,至少对于您要实现的目标而言。
当你想到它时,拦截器不应该绑定到实现,因为它代理了一个接口,那么你为什么不把 [Cache]
属性放在接口级别?
使用你的代码,我在界面上可以成功检索它。
编辑:
好的,我整理了一个 repository on GitHub,它使用您提到的特定版本的 NuGet 包,并展示了如何检索拦截方法的属性。
提醒一下,这里是使用过的 NuGet 包:
Microsoft.AspNet.Mvc
v5.2.3Autofac
v4.3.0Autofac.Mvc5
4.0.0Autofac.Extras.DynamicProxy
v4.2.1Castle.Core
v4.0.0
我创建了 2 个 query 接口,IMyQuery
和 IMySecondQuery
。请注意,正如我最初的回答中提到的,[Cache]
属性位于接口方法上,而不是实现 类.
public interface IMyQuery
{
[Cache(60000)]
string GetName();
}
public interface IMySecondQuery
{
[Cache(1000)]
string GetSecondName();
}
然后我们有 2 个非常基本的实现 类。完全不相关,但为了完整起见:
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>
方法是 System.Reflection
命名空间中 MemberInfo
的扩展方法。
让我们继续在 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/myquery
和 http://localhost:62440/home/myquery
。
确实命中了拦截器,找到了[Cache]
属性。在 Visual Studio 输出 window 中,确实显示:
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 以便第一个查询调用第二个。
它仍然有效。你真的应该努力把一些代码放在这个问题上。