真正的拦截器对我的c#类做了什么?

我被要求在我的asp.net web应用程序中实现城堡动态代理,我正在阅读几篇文章,我从Castle Project和Code Project获得了关于asp.net web应用程序中的城堡动态代理….

这两篇文章都与创建拦截器有关,但我无法理解为什么拦截器与类一起使用….为什么我应该拦截我的类,哪个行为正常?

假设你的class级需要为某项操作做3件事:

  1. 执行安全检查;
  2. 记录方法调用;
  3. 缓存结果。

让我们进一步假设您的类对您配置安全性,日志记录或缓存的具体方式一无所知。 你需要依赖这些东西的抽象。

有几种方法可以解决它。 一种方法是设置一堆接口并使用构造函数注入:

public class OrderService : IOrderService { private readonly IAuthorizationService auth; private readonly ILogger logger; private readonly ICache cache; public OrderService(IAuthorizationService auth, ILogger logger, ICache cache) { if (auth == null) throw new ArgumentNullException("auth"); if (logger == null) throw new ArgumentNullException("logger"); if (cache == null) throw new ArgumentNullException("cache"); this.auth = auth; this.logger = logger; this.cache = cache; } public Order GetOrder(int orderID) { auth.AssertPermission("GetOrder"); logger.LogInfo("GetOrder:{0}", orderID); string cacheKey = string.Format("GetOrder-{0}", orderID); if (cache.Contains(cacheKey)) return (Order)cache[cacheKey]; Order order = LookupOrderInDatabase(orderID); cache[cacheKey] = order; return order; } } 

这不是可怕的代码,但想想我们引入的问题:

  • 如果没有所有三个依赖项, OrderService类就无法运行。 如果我们想要这样做,我们需要开始在任何地方使用空检查来编写代码。

  • 我们正在编写大量额外代码来执行相对简单的操作(查找订单)。

  • 所有这些样板代码都必须在每个方法中重复,从而形成一个非常大,丑陋,容易出错的实现。

这是一个更容易维护的类:

 public class OrderService : IOrderService { [Authorize] [Log] [Cache("GetOrder-{0}")] public virtual Order GetOrder(int orderID) { return LookupOrderInDatabase(orderID); } } 

在面向方面编程中 ,这些属性称为连接点 ,其完整集合称为“ 点切割” 。

我们不是一遍又一遍地实际编写依赖代码,而是留下“提示”,即应该为此方法执行一些额外的操作。

当然,这些属性必须在某个时候变成代码,但是您可以通过为OrderService创建代理来推迟一直到主应用程序代码(请注意, GetOrder方法已经变为virtual因为它需要是覆盖服务),并拦截 GetOrder方法。

编写拦截器可能就像这样简单:

 public class LoggingInterceptor : IInterceptor { public void Intercept(IInvocation invocation) { if (Attribute.IsDefined(invocation.Method, typeof(LogAttribute)) { Console.Writeline("Method called: "+ invocation.Method.Name); } invocation.Proceed(); } } 

创建代理将是:

 var generator = new ProxyGenerator(); var orderService = (IOrderService)generator.CreateClassProxy(typeof(OrderService), new LoggingInterceptor()); 

这不仅是重复性较低的代码,而且它完全消除了实际的依赖性 ,因为看看我们做了什么 – 我们甚至没有授权或缓存系统,但系统仍在运行。 稍后我们可以通过注册另一个拦截器并检查AuthorizeAttributeCacheAttribute来插入授权和缓存逻辑。

希望这能解释“为什么”。

边栏:正如KrzysztofKoźmic评论的那样,使用这样的动态拦截器并不是DP的“最佳实践”。 在生产代码中,您不希望拦截器运行不必要的方法,因此请使用IInterceptorSelector 。

你使用Castle-DynamicProxy的原因是所谓的面向方面编程。 它允许您将代码插入代码的标准操作流程中,而无需依赖代码本身。

一个简单的例子就是一如既往地记录。 你会在一个类中创建一个DynamicProxy,你会遇到错误,它将数据记录到方法中并捕获任何exception,然后记录exception。

使用intercepter您当前的代码不知道它是否存在(假设您的软件以正确的接口解耦方式构建)并且您可以通过控制容器的反转来更改类的注册,以使用代理类而不必在代码中更改单行其他位置。 然后,当您解决错误时,您可以关闭代理。

使用NHibernate可以看到更高级的代理用法,其中所有延迟加载都是通过代理处理的。