.NET Core:在方法之前和之后执行的属性

时间:2017-09-01 12:56:29

标签: c# .net-core

在Java中,可以使用AspectJ在执行方法之前和之后使用method annotations添加行为。由于C#属性似乎非常相似,我想知道是否有可能实现类似的行为。我正在查看几个教程和其他来源(123),但没有一个帮助过我。

我想也许我可以通过将代码插入到Attribute构造函数中并使其成为一次性来模仿行为,如下所示:

[AttributeUsage(AttributeTargets.Method)]
public class MyWritingAttribute : Attribute, IDisposable
{
    public MyWritingAttribute()
    {
        Console.WriteLine("Attribute created");
    }

    public void Dispose()
    {
        Console.WriteLine("Attribute disposed");
    }
}

但是,当使用这样的属性时,控制台中只显示 Hello world!

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

    [MyWriting]
    private static void SayHelloWorld()
    {
        Console.WriteLine("Hello World!");
    }
}

我在想,可能在属性中无法访问Console,但即使用throw new Exception()表达式替换它,也不会抛出任何异常。 EF的StringLengthAttribute如何工作,但我的属性甚至没有被实例化?如何在装饰方法之前和之后运行属性?

4 个答案:

答案 0 :(得分:3)

就像Java和AspectJ一样,你需要单独的AoP工具来在.NET中注入这样的代码。

PostSharp就是这样一个工具,可能是最知名的。我相信他们从版本5开始就支持.NET核心。

答案 1 :(得分:3)

您需要一些能够适当处理您的属性的框架。仅因为该属性存在并不意味着它将具有任何影响。

我写了一些简单的引擎来做到这一点。它将确定传递的action上是否存在该属性,如果是,则获取反射的方法以执行它们。

class Engine
{
    public void Execute(Action action)
    {
        var attr = action.Method.GetCustomAttributes(typeof(MyAttribute), true).First() as MyAttribute;
        var method1 = action.Target.GetType().GetMethod(attr.PreAction);
        var method2 = action.Target.GetType().GetMethod(attr.PostAction);

        // now first invoke the pre-action method
        method1.Invoke(null, null);
        // the actual action
        action();
        // the post-action
        method2.Invoke(null, null);
    }
}
public class MyAttribute : Attribute
{
    public string PreAction;
    public string PostAction;
}

当然你需要一些零空格,例如如果方法不存在或不是静态的。

现在你必须用属性装饰你的动作:

class MyClass
{
    [MyAttribute(PreAction = "Handler1", PostAction = "Handler2")]
    public void DoSomething()
    {

    }

    public static void Handler1()
    {
        Console.WriteLine("Pre");
    }
    public static void Handler2()
    {
        Console.WriteLine("Post");
    }
}

最后,您可以在我们的引擎中执行该方法:

var engine = new Engine();
var m = new MayClass();
engine.Execute(m.DoSomething);

答案 2 :(得分:2)

该问题类似于Run a method before all methods of a class,因此相同的答案适用于两者。 使用https://github.com/Fody/Fody。许可模式基于自愿捐款,这使其成为PostSharp的更好选择,这对我来说有点贵。

[module: Interceptor]
namespace GenericLogging
{

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Assembly | AttributeTargets.Module)]
    public class InterceptorAttribute : Attribute, IMethodDecorator
    {
        // instance, method and args can be captured here and stored in attribute instance fields
        // for future usage in OnEntry/OnExit/OnException
        public void Init(object instance, MethodBase method, object[] args)
        {
            Console.WriteLine(string.Format("Init: {0} [{1}]", method.DeclaringType.FullName + "." + method.Name, args.Length));
        }

        public void OnEntry()
        {
            Console.WriteLine("OnEntry");
        }

        public void OnExit()
        {
            Console.WriteLine("OnExit");
        }

        public void OnException(Exception exception)
        {
            Console.WriteLine(string.Format("OnException: {0}: {1}", exception.GetType(), exception.Message));
        }
    }

    public class Sample
    {
        [Interceptor]
        public void Method(int test)
        {
            Console.WriteLine("Your Code");
        }
    }
}

[TestMethod]
public void TestMethod2()
{
    Sample t = new Sample();
    t.Method(1);
}

答案 3 :(得分:1)

这可以使用DynamicProxy来完成。这里有关于如何执行此操作的说明AOP in .Net Core using AutoFac and DynamicProxy

存在一种内存缓存技术的实现,其中逻辑在调用方法之前执行。这可以扩展到检查是否存在这样的属性

var attribute = Attribute.GetCustomAttribute(invocation.MethodInvocationTarget, typeof(CachedAttribute)) as CachedAttribute;
if (attribute != null)
{
  ...
}

上面的代码可以在Interceptor实现中的Intercept方法内。 CachedAttribute将是您的属性。