如何使用Castle Core或其他库(只是免费库)为Inter Cutting Concerns编写拦截器(AOP)

时间:2014-12-14 19:11:31

标签: c# aop interceptor castle-dynamicproxy interception

我希望像Logging,Exception等一样具有这样的属性:

public class MyService
{

[Log] // Interception (AOP)
[ExceptionHandler] // Interception (AOP)
public void DoSomething()
   {

   }
}

我知道我可以用postsharp编写这些代码,但我想用Castle Core等免费库编写这些拦截...

任何人都可以帮助我并为此目的写一个样本吗? 我需要一个非常简单的学习概念样本

4 个答案:

答案 0 :(得分:3)

Autofac是一个免费的IoC容器。我将Autofac与Autofac.Extras.DynamicProxy2 nugetdocs一起使用。

假设您知道为什么以及何时(而不是)使用拦截器,并且您想拦截某些功能:

public class FooService : IFooService
{
    public void MoreFoo()
    {
        DoSomething();
    }
    public void LessFoo()
    {
        DoSomethingElse();
    }
}

需要“有线”。我喜欢属性,因为您不需要在IoC容器布线中明确指定拦截器。您只需指定要注意的属性:

[Intercept(typeof(Logger)]
public class FooService : IFooService { ... }

并连线:

var builder = new ContainerBuilder();
builder.RegisterType<FooService>()
   .EnableClassInterceptors();

然后在另一个文件中创建Logger拦截器:

class Logger : IInterceptor
{
    public void Intercept(IInvocation invocation) // implements the IInterceptor interface
    {
        _loggerService.Log("calling " + invocation.Method.Name);
        invocation.Proceed();
        _loggerService.Log("finished " + invocation.Method.Name);
    }
}

如您所见,您可以创建计时器,try-catch块等等。数据库环境和其他可支配资源是一个有趣的:

class Logger : IInterceptor
{
    public void Intercept(IInvocation invocation) // implements the IInterceptor interface
    {
        using (var someThing = new SomeResource())
        {
            invocation.Proceed();
        }
    }
}

通常使用这样的资源,您需要在方法中使用someThing。这是另一个问题的主题! (请参阅invocation.SetArgumentValue或invocation.TargetType.GetProperties()以与封闭类进行通信。我对此并不是很满意,因此其他人的一些评论会有所帮助)

然后,以日志记录为例:

void ManageFoo()
{
    // sorry for the messy code, what else can I do?!
    _logger("more foo please");
    _fooService.MoreFoo();
    _logger("less foo please");
    _fooService.LessFoo();
    _logger("enough foo");
}

ManageFoo方法的实际问题在所有日志记录中都丢失了(添加安全性和其他问题,最终可能会造成很大的混乱)。

现在你可以像这样重写它:

void ManageFoo()
{
    _fooService.MoreFoo();
    _fooService.LessFoo();
}

答案 1 :(得分:1)

Java具有使用AspectJ和编织的AOP(具有代理的LTW加载时间,以及CTW的编译时间) C#(城堡)具有拦截器,也使用(动态)代理。您可以将其视为LTW变体。

我在c#中使用了此设置。 这不是什么大魔术,而且代码也很有限。

Autofac 5.1.0
Autofac.Extras.DynamicProxy 5.0.0
Castle.Core 4.4.0

诀窍是

  1. 定义一些服装,用作Intersceptor属性
  2. 定义一个拦截器,该拦截器检查名为的方法上的属性
  3. 使用方法和接口方法上的属性定义接口
  4. 定义接口的实现类
  5. 使用autofac注册整个设置
  6. 测试/运行/运行

1)定义一些服饰

using System;
[AttributeUsage(
AttributeTargets.Method,
AllowMultiple = true)]

public class SomeAttribute : Attribute
{
    public long Id { get; set; }
}

2)定义Castle动态拦截器(也称为代理)

using Castle.DynamicProxy;
using System;
public class SomeInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
         if (Attribute.IsDefined(invocation.Method, typeof(SomeAttribute)))
        {
            Console.Write("Method called: " + invocation.Method.Name);
        }
        invocation.Proceed();
    }
}

现在,使用接口创建对象(不要忘记,将属性放在接口上,而不是在impl上!)

3)定义接口

public interface AOPTest
{
    [Some(Id = 10)]
    void DoSomething();
}

4)定义实现:

public class AOPTestImpl : AOPTest
{
    public void DoSomething()
    {
    }
}

5)用autofac注册整个设置

builder.RegisterType<AOPTestImpl>()
            .As<AOPTest>()
            .EnableInterfaceInterceptors()
            .InterceptedBy(typeof(SomeInterceptor));

builder.RegisterType<SomeInterceptor>().AsSelf();

6)测试/运行/运行:运行整个设置:

using Autofac;
using Autofac.Extras.DynamicProxy;
using (var scope = bootstrap.BootStrap.Container.BeginLifetimeScope())
{
     var aOPTest = scope.Resolve<AOPTest>();
     aOPTest.DoSomething();
}

我不知道它是如何工作的,但是这个想法是:

界面->代理->实现 因此,如果您通过接口调用实现,则代理/拦截器介于两者之间。

注意:如果在doSomething()方法内调用其他也需要拦截的代码,则可能需要一个autofac类拦截器EnableClassInterceptors

注意:这不是世界上最快的解决方案。某些过滤器拦截器可能更快,而像Fody或PostSharp这样的编译时间编织可能更快。但这会很多次。

注意:如果需要在方法开始之前完成某些操作,请在调用之前对它进行编码。Proceed();如果您需要在最后完成某件事,请在调用@ After.Proceed()之后进行编码:

@之前

DoSomething(){...}

@之后

答案 2 :(得分:0)

此库可以满足您的需求https://github.com/pamidur/aspect-injector

[LogCall]
public void Calculate() 
{ 
    Console.WriteLine("Calculated");
}

ps。无耻的自我广告

答案 3 :(得分:0)

如果要使用运行时aop,可以尝试https://fs7744.github.io/Norns.Urd/index.html

这非常简单,就像一个拦截器:

public class ConsoleInterceptor : AbstractInterceptor
 {
     public override async Task InvokeAsync(AspectContext context, AsyncAspectDelegate next)
     {
         Console.WriteLine($"{context.Service.GetType().GetReflector().FullDisplayName}.{context.Method.GetReflector().DisplayName}");
         await next(context);
     }
 }