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

标签 c# class interceptor castle-dynamicproxy

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

这两篇文章都与创建拦截器有关,但我不明白为什么拦截器与类一起使用......为什么我应该拦截行为正常的类?

最佳答案

假设您的类需要为某个操作做 3 件事:

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

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

有几种方法可以解决这个问题。一种方法是设置一堆接口(interface)并使用构造函数注入(inject):

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);
    }
}

Aspect Oriented Programming ,这些属性称为 Join Points , 完整的一组称为 Point Cut .

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

当然,这些属性有时必须转化为代码,但您可以通过为OrderService(请注意,GetOrder 方法已成为虚拟,因为它需要为服务覆盖),以及拦截 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 来插入授权和缓存逻辑。

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

边栏:正如 Krzysztof Koźmic 评论的那样,使用这样的动态拦截器并不是 DP 的“最佳实践”。在生产代码中,您不想让拦截器为不必要的方法运行,所以使用 IInterceptorSelector。相反。

关于c# - 真正的拦截器对我的 C# 类做了什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2592787/

相关文章:

java - 如何通过注解设置Alias Interceptor的参数

c# - 静态事件处理程序和非静态事件处理程序有什么区别

c# - 如何声明 3 列 4 行的数组

c# - Document.Body.Descendants<SdtBlock>() 不返回所有 *sdt* 元素

c# - 从字符串中提取字符(正则表达式)

java - 您如何知道何时需要添加单独的类?

objective-c - 无法将 'class' 用作 NSDictionary 中的键

phpunit - 测试套件的名称可以与现有类相同吗?

reactjs - 使用 axios interceptors.request 时,error.response.data 未定义

java - Spring 3.0如何使用方法拦截器?