ASP.NET MVC3控制器AOP代理不拦截所有方法,只有IController.Execute

时间:2012-01-20 08:41:59

标签: c# asp.net-mvc-3 castle-windsor aop castle-dynamicproxy

我有一个包含多个层的项目 - 其中包括Web前端(ASP.NET MVC3)和服务后端(主要是业务逻辑)。这个项目已经有几个月了,所以一切都按预期工作。现在我尝试使用自定义[Log]属性为某些MVC3控制器方法添加日志记录方面。

我使用Castle Windsor进行依赖注入。为了获得日志记录方面,我使用了Castle DynamicProxySNAP。正在使用KrzysztofKoźmic的有用教程中的WindsorControllerFactory来解析控制器 - 但我对其进行了修改以寻找控制器的默认接口(见下文)。

在我的服务层:

[Log(LoggingLevel.Info)]
public void Save(MyBusinessDto dto)
{
    // business logic and other checks

    this.repository.Save(mbo);
}

在我的网络前端IWindsorInstaller为控制器:

private static BasedOnDescriptor FindControllers()
{
    return AllTypes
            .FromThisAssembly()
            .BasedOn<IController>()
            .WithService.DefaultInterface();
}

在我的(稍微定制的)WindsorControllerFactory中查找控制器的默认界面:

protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
    if (controllerType == null)
    {
        throw new HttpException(404, string.Format(Error404, requestContext.HttpContext.Request.Path));
    }

    string controllerName = controllerType.Name;
    string defaultInterfaceName = 'I' + controllerName;
    Type defaultInterface = controllerType.GetInterface(defaultInterfaceName);

    object controller = this.kernel.Resolve(defaultInterface);

    return (IController)controller;
}

在我的控制器中:

public class MyBusinessController : MyBusinessControllerBase, IMyBusinessController
{
    [Log(LoggingLevel.Debug)]
    public ActionResult CreateOrUpdate(MyBusinessFormModel fm)
    {
        // Convert form model to data transfer object,
        // perform validation and other checks

        this.service.Save(dto);

        return View(fm);
    }
}

这一切在服务项目中都可以正常工作,但在控制器中,方法没有被截获。

  • 我已确认WindsorControllerFactory返回代理控制器。
  • 我已确认控制器已注册拦截器。
  • 我已确认SNAP中的MasterProxy拦截了控制器 - 但它只拦截了IController.Execute(RequestContext requestContext)

如何拦截具有[Log]属性的所有控制器方法?

更新1:我考虑过直接使用DynamicProxy而不是SNAP,但这也是让它适用于控制器的第二步。

更新2 + 4:似乎SNAP back on github中缺少

更新3:这是我在Visual Studio调试器中看到的WindsorControllerFactory(见上文)。被检查的controller变量是返回给MVC的变量,它确实被代理了。

  • controller {Castle.Proxies.IMyBusinessControllerProxy}
    • __interceptors {Castle.DynamicProxy.IInterceptor [1]}
      • [0] {Snap.MasterProxy}
    • __target {My.Business.Web.Controllers.MyBusinessController}
      • service {Castle.Proxies.IMyBusinessServiceProxy}
      • (其他构造者注射)
    • MyInjectedProperty {My.Business.Useful.MyOtherType}

2 个答案:

答案 0 :(得分:4)

IController GetControllerInstance(...)中,不要提供接口代理,请使用virtual方法提供类代理。

IController GetControllerInstance(...)返回的控制器中的用户实现的方法不会通过代理IMyBusinessController接口访问,而是从IController转换为控制器的实际类;例如MyBusinessController。改为使用类代理,使MVC3的强制转换返回代理。此外,将方法标记为virtual,否则拦截代理将无法拦截方法调用并检查自定义属性。

在控制器中,使用属性<{p>>将virtual添加到方法中

public class MyBusinessController : MyBusinessControllerBase, IMyBusinessController
{
    [Log(LoggingLevel.Debug)]
    public virtual ActionResult CreateOrUpdate(MyBusinessFormModel fm)
    {
        // Convert form model to data transfer object,
        // perform validation and other checks

        this.service.Save(dto);

        return View(fm);
    }
}

为什么只有Execute(...)被截获? IController界面只包含Execute(...)。在返回的控制器接口代理上调用Execute,因此可以截获它。但是一旦MVC3的内部ControllerBase.Execute(...)接到调用,它就会对ControllerFactory所期望的类执行强制转换。

问题类似于this leaking,因为它们都绕过了接口代理。我想它可以通过多种方式解决;也许通过创建一个自定义类型转换器,从工厂中的接口代理目标创建一个类代理,一个聪明的Windsor配置等。

KrzysztofKoźmic的IController installerWindsorControllerFactory应该开箱即用。可以在更大的图片中推荐接口代理(并且它们在控制器中使用拦截器之前工作良好)但在这种情况下可能有理由不去那么远,以避免进一步的副作用。

感谢Marius指出我正确的方向!

答案 1 :(得分:2)

由于DynamicProxy(SNAP使用dynamicproxy)无法拦截非虚方法,我猜测返回的代理是控制器的派生类,因此忽略非虚方法。您需要制作SNAP(不知道它是如何工作的)返回带有目标的接口代理(您的实现)或者只是尝试使您的控制器方法成为虚拟。