You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于使用Simple Injector为ASP.NET MVC控制器添加拦截器的咨询

Great question! Let's break this down into two parts: why that interface-only restriction exists, and how to add interception to your ASP.NET MVC controllers with Simple Injector.


Why the Interface-Only Restriction?

The limitation you’re hitting comes down to how Simple Injector’s default interception works under the hood—it relies on .NET’s System.Runtime.Remoting.Proxies.RealProxy type. As the comment in the code hints, RealProxy only supports creating proxies for interfaces, not concrete classes.

Concrete types can technically be proxied with RealProxy, but only if they inherit from MarshalByRefObject—a requirement that’s completely impractical for MVC controllers. It introduces unnecessary remoting overhead, doesn’t align with standard MVC controller design, and would force you to refactor all your controllers just to enable interception. Simple Injector enforces the interface check to avoid pushing you into this messy, inefficient solution.


Practical Solutions for MVC Controller Interception

Since controllers are concrete types inheriting from Controller, here are three reliable ways to add interception logic:

This is the cleanest, most framework-agnostic approach. You’ll wrap your controllers in a decorator that adds interception logic around the controller’s core execution.

  1. Create a controller decorator base class:
public class ControllerInterceptorDecorator : Controller
{
    private readonly Controller _targetController;
    private readonly IActionInterceptor _interceptor;

    public ControllerInterceptorDecorator(Controller targetController, IActionInterceptor interceptor)
    {
        _targetController = targetController;
        _interceptor = interceptor;
        // Pass through the controller context to the wrapped instance
        ControllerContext = targetController.ControllerContext;
    }

    // Override the core execution method for MVC controllers
    protected override void ExecuteCore()
    {
        var actionName = ControllerContext.RouteData.Values["action"].ToString();

        // Pre-execution interception logic
        _interceptor.BeforeAction(actionName);

        try
        {
            // Run the original controller's action
            _targetController.ExecuteCore();
        }
        catch (Exception ex)
        {
            // Exception handling logic
            _interceptor.OnActionException(actionName, ex);
            throw;
        }
        finally
        {
            // Post-execution logic
            _interceptor.AfterAction(actionName);
        }
    }
}
  1. Implement your interceptor logic:
public interface IActionInterceptor
{
    void BeforeAction(string actionName);
    void AfterAction(string actionName);
    void OnActionException(string actionName, Exception ex);
}

public class LoggingActionInterceptor : IActionInterceptor
{
    private readonly ILogger _logger;

    public LoggingActionInterceptor(ILogger logger)
    {
        _logger = logger;
    }

    public void BeforeAction(string actionName) => 
        _logger.LogInformation($"Starting action: {actionName}");

    public void AfterAction(string actionName) => 
        _logger.LogInformation($"Completed action: {actionName}");

    public void OnActionException(string actionName, Exception ex) => 
        _logger.LogError(ex, $"Action failed: {actionName}");
}
  1. Register everything in your Simple Injector container:
// First, register all MVC controllers
container.RegisterMvcControllers(Assembly.GetExecutingAssembly());

// Register your interceptor
container.Register<IActionInterceptor, LoggingActionInterceptor>(Lifestyle.Singleton);

// Apply the decorator to all controllers
container.RegisterDecorator(typeof(Controller), typeof(ControllerInterceptorDecorator));

Solution 2: Use Castle DynamicProxy for Granular Method Interception

If you need to intercept individual action methods (instead of the entire controller execution), integrate Simple Injector with Castle DynamicProxy, which supports proxying concrete types.

  1. Install the required NuGet packages:
Install-Package SimpleInjector.Integration.CastleWindsor
Install-Package Castle.Core
  1. Configure DynamicProxy and your interceptor:
// Enable DynamicProxy interception in Simple Injector
container.EnableDynamicProxy();

// Implement a Castle IInterceptor
public class MethodLevelInterceptor : IInterceptor
{
    private readonly ILogger _logger;

    public MethodLevelInterceptor(ILogger logger)
    {
        _logger = logger;
    }

    public void Intercept(IInvocation invocation)
    {
        // Only intercept public action methods (skip helper methods)
        if (invocation.Method.IsPublic && !invocation.Method.IsSpecialName)
        {
            _logger.LogInformation($"Executing method: {invocation.Method.Name}");
        }

        // Run the original method
        invocation.Proceed();

        if (invocation.Method.IsPublic && !invocation.Method.IsSpecialName)
        {
            _logger.LogInformation($"Completed method: {invocation.Method.Name}");
        }
    }
}

// Register the interceptor and apply it to all controllers
container.Register<IInterceptor, MethodLevelInterceptor>(Lifestyle.Singleton);
container.InterceptWith<MethodLevelInterceptor>(type => type.IsSubclassOf(typeof(Controller)));

Solution 3: Custom Controller Activator

For full control over controller creation, implement a custom IControllerActivator that wraps controllers with interception logic when they’re instantiated.

  1. Create the activator:
public class InterceptingActivator : IControllerActivator
{
    private readonly Container _container;
    private readonly IActionInterceptor _interceptor;

    public InterceptingActivator(Container container, IActionInterceptor interceptor)
    {
        _container = container;
        _interceptor = interceptor;
    }

    public IController Create(RequestContext requestContext, Type controllerType)
    {
        // Resolve the raw controller from Simple Injector
        var controller = (Controller)_container.GetInstance(controllerType);
        // Wrap it with your interceptor decorator
        return new ControllerInterceptorDecorator(controller, _interceptor);
    }
}
  1. Register the activator in your MVC setup:
// In Global.asax or Startup.cs
ControllerBuilder.Current.SetControllerFactory(
    new DefaultControllerFactory(new InterceptingActivator(container, container.GetInstance<IActionInterceptor>()))
);

内容的提问来源于stack exchange,提问作者boger

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 04:32:08