我有一个包含多个层的项目 - 其中包括Web前端(ASP.NET MVC3)和服务后端(主要是业务逻辑)。这个项目已经有几个月了,所以一切都按预期工作。现在我尝试使用自定义[Log]
属性为某些MVC3控制器方法添加日志记录方面。
我使用Castle Windsor进行依赖注入。为了获得日志记录方面,我使用了Castle DynamicProxy到SNAP。正在使用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
返回代理控制器。MasterProxy
拦截了控制器 - 但它只拦截了IController.Execute(RequestContext requestContext)
。 如何拦截具有[Log]
属性的所有控制器方法?
更新1:我考虑过直接使用DynamicProxy而不是SNAP,但这也是让它适用于控制器的第二步。
更新2 + 4:似乎SNAP 。
更新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} 答案 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 installer和WindsorControllerFactory
应该开箱即用。可以在更大的图片中推荐接口代理(并且它们在控制器中使用拦截器之前工作良好)但在这种情况下可能有理由不去那么远,以避免进一步的副作用。
感谢Marius指出我正确的方向!
答案 1 :(得分:2)
由于DynamicProxy(SNAP使用dynamicproxy)无法拦截非虚方法,我猜测返回的代理是控制器的派生类,因此忽略非虚方法。您需要制作SNAP(不知道它是如何工作的)返回带有目标的接口代理(您的实现)或者只是尝试使您的控制器方法成为虚拟。