如何获取自定义属性在执行时附加到的函数/操作名称?

时间:2015-09-11 19:13:36

标签: c# asp.net .net asp.net-mvc action-filter

理想情况下,我想创建一个继承自ActionFilterAttribute的过滤器,我可以在Global.asax中应用该过滤器,该过滤器将为我的应用程序中的所有操作创建性能计数器。这个问题很简单,但问题是我希望性能计数器在其名称中包含它们附加到的操作的方法签名。但是,我找不到一种方法来提取构造期间附加属性的方法的方法名称。这导致我必须单独将属性应用于每个操作,并将其签名作为参数传递。但是,这会带来明显的问题(即方法签名的更新不会自动与perf计数器命名同步)。

为了简化问题,如果我将属性附加到任何类型的方法,我可以访问它附加到的方法的名称/签名吗?我正在寻找适用于不是从ActionFilterAttribute派生的属性的通用解决方案。

public class SomeAttribute : ActionFilterAttribute
{
    public string FunctionSignature { get; set; }

    public SomeAttribute() 
    {
        this.FunctionName = { HOW DO I GET THE NAME OF THE METHOD I'M ON WITHOUT PASSING IT IN AS AN INPUT ARGUMENT? }
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        // Some code to update perf counter(s) with recorded time that will use string.Format("{0}: Avg. Time or Something", this.FunctionSignature).
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    {
        // Some code to record time.
    }
}

[SomeAttribute]
public void SomeMethod()
{
    // Some code.
}

4 个答案:

答案 0 :(得分:4)

查找执行操作的名称:

var actionName = filterContext.ActionDescriptor.ActionName;

或者

var actionName = filterContext.RouteData.Values["action"] as string

查找参数(名称,类型,DefaultValue):

var parameters = filterContext.ActionDescriptor.GetParameters();

查找参数值:

 var value= filterContext.ActionParameters["parameterName"]; 

答案 1 :(得分:1)

我假设您的方法名称与filterContext.ActionDescriptor.ActionName相同。

您可以从filterContext.Controller获取Controller实例。

因此,使用类和方法名称可以获得签名,但不能在构造函数中。

答案 2 :(得分:1)

我可以想象两种选择。您可以在加载的程序集中反映类中的所有类型 - 不是非常直接但有效。问题是 - 我不确定有趣的程序集是否及时加载 - 您可能必须使用配置信息作为指导主动加载它们。

属性可以是您可以反射询问的各种MethodInfo / PropertyInfo对象的查询。然后,使用MemberInfo.GetCustomeAttributes查询属性。

或者,代替global.asax,您可以让有趣的类型在静态初始化期间注册自己进行检查。

答案 3 :(得分:1)

据我所知,您需要通用解决方案,与ActionFilterAttribute或asp.net完全无关。然后你可以使用面向方面编程,而对于.NET的最佳实现是PostSharp。该库的免费版本足以实现您的目标。例如:

class Program
{
    static void Main(string[] args)
    {
        Test();
        Console.ReadKey();
    }

    [Measure]
    public static void Test() {
        Thread.Sleep(1000);
    }
}

[Serializable]
public sealed class MeasureAttribute : OnMethodBoundaryAspect
{        
    private string _methodName;
    [NonSerialized]
    private Stopwatch _watch;
    public override void CompileTimeInitialize(MethodBase method, AspectInfo aspectInfo) {
        base.CompileTimeInitialize(method, aspectInfo);
        // save method name at _compile_ time
        _methodName = method.Name;
    }


    public override void OnEntry(MethodExecutionArgs args) {
        base.OnEntry(args);
        // here you have access to everything about method
        _watch = Stopwatch.StartNew();
    }

    public override void OnExit(MethodExecutionArgs args) {
        base.OnExit(args);
        if (_watch != null) {
            _watch.Stop();
            Console.WriteLine("Method {0} took {1}ms", _methodName, _watch.ElapsedMilliseconds);
        }
    }

    public override void OnException(MethodExecutionArgs args) {
        base.OnException(args);
        // do what you want on exception
    }
}

在这里,我们创建MeasureAttribute,您可以在任何方法和许多点上的拦截方法调用中应用它。甚至,您甚至可以根据某些条件(即给定类或整个程序集中的所有方法,或其他任何方法)动态地将其应用于所有方法。它还允许您在编译时间中保存一些信息,以提高性能。在上面的例子中,我们在编译期间保存了一次方法名称。

PostSharp(和AOP一般)可以做的远不止这些。