我被要求在我的 asp.net web 应用程序中实现城堡动态代理,我正在阅读从 Castle Project 获得的几篇文章。和 Code Project关于 asp.net web 应用程序中的城堡动态代理....
这两篇文章都与创建拦截器有关,但我不明白为什么拦截器与类一起使用......为什么我应该拦截行为正常的类?
最佳答案
假设您的类需要为某个操作做 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());
这不仅减少了很多重复代码,而且它完全消除了实际的依赖性,因为看看我们做了什么 - 我们甚至没有授权或缓存系统,但系统仍在运行。我们可以稍后通过注册另一个拦截器并检查 AuthorizeAttribute
或 CacheAttribute
来插入授权和缓存逻辑。
希望这能解释“为什么”。
边栏:正如 Krzysztof Koźmic 评论的那样,使用这样的动态拦截器并不是 DP 的“最佳实践”。在生产代码中,您不想让拦截器为不必要的方法运行,所以使用 IInterceptorSelector。相反。
关于c# - 真正的拦截器对我的 C# 类做了什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2592787/