我被要求在我的 asp.net web 应用程序中实现城堡动态代理,我正在阅读我从Castle Project和Code Project获得的关于 asp.net web 应用程序中的城堡动态代理的几篇文章....
两篇文章都与创建拦截器有关,但我不明白为什么拦截器与类一起使用......为什么我应该拦截行为正确的类?
我被要求在我的 asp.net web 应用程序中实现城堡动态代理,我正在阅读我从Castle Project和Code Project获得的关于 asp.net web 应用程序中的城堡动态代理的几篇文章....
两篇文章都与创建拦截器有关,但我不明白为什么拦截器与类一起使用......为什么我应该拦截行为正确的类?
假设您的班级需要为某个操作做 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());
这不仅减少了重复代码,而且完全消除了实际的依赖关系,因为看看我们所做的——我们甚至还没有授权或缓存系统,但系统仍然运行。我们可以稍后通过注册另一个拦截器并检查AuthorizeAttribute
or来插入授权和缓存逻辑CacheAttribute
。
希望这能解释“为什么”。
边栏:正如 Krzysztof Koźmic 评论的那样,使用这样的动态拦截器并不是 DP “最佳实践”。在生产代码中,您不希望拦截器运行不必要的方法,因此请改用IInterceptorSelector。
您使用 Castle-DynamicProxy 的原因是所谓的面向方面编程。它使您可以将代码插入代码的标准操作流程,而无需依赖代码本身。
一个简单的例子是一如既往的记录。您将围绕一个您有错误的类创建一个 DynamicProxy,它会记录进入方法的数据并捕获任何异常,然后记录异常。
使用拦截器,您当前的代码不知道它存在(假设您的软件以解耦方式构建,并且接口正确)并且您可以使用控制容器的反转来更改类的注册以使用代理类,而无需在代码中的其他地方更改一行。然后,当您解决错误时,您可以关闭代理。
使用 NHibernate 可以看到更高级的代理使用,其中所有的延迟加载都是通过代理处理的。