使用 base class 中的参数解析构造函数参数

Resolve constructor argument with parameter from base class

我有一个自定义的 ASP.NET MVC 控制器,它从用户服务中检索操作。我想使用依赖注入将操作 属性 传递给场景服务。

public abstract class BaseController : Controller {
    protected IUserService userService;
    public OperationWrapper operations { get; private set; }
    public BaseController(IUserService userService) {
        this.userService = userService;
        this.operations = userService.GetOperations(HttpContext.Current.User.Identity.Name);
    }
}

public abstract class ScenarioController : BaseController {
    protected IScenarioService scenarioService;
    public ScenarioController(IScenarioService scenarioService, IUserService userService)
        : base(userService) {
        this.scenarioService = scenarioService;
    }
}

public class ScenarioService : IScenarioService {
    private OperationWrapper operations;
    public ScenarioService(OperationWrapper operations) {
        this.repo = repo;
        this.operations = operations;
    }
}

这是我的温莎安装程序。

public class Installer : IWindsorInstaller {
    public void Install(IWindsorContainer container, IConfigurationStore store) {
        container.Register(Classes.FromThisAssembly()
                        .BasedOn<IController>());

        container.Register(Classes.FromThisAssembly()
                            .Where(x => x.Name.EndsWith("Service"))
                            .WithService.DefaultInterfaces()
                            .LifestyleTransient());
    }
}

我很确定我在几年前用 Ninject 做过类似的事情。我需要在安装程序中添加什么才能使其正常工作?有可能吗?

您应该在 global.asax

的 Application_Start 方法中设置依赖解析器
System.Web.MVC.DependencyResolver.SetResolver(your windsor resolver)

创建一个继承自 DefaultControllerFactory 的 class。这样的事情会做:

public class WindsorControllerFactory : DefaultControllerFactory
{
    public WindsorControllerFactory(IKernel kernel)
    {
        _kernel = kernel;
    }

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType == null)
        {
            throw new HttpException(
                404,
                String.Format(
                    CultureInfo.CurrentCulture,
                    "The controller for path '{0}' was not found or does not implement IController.",
                    requestContext.HttpContext.Request.Path
                    )
                );
        }

        return (IController)_kernel.Resolve(controllerType);    
    }

    public override void ReleaseController(IController controller)
    {
        Kernel.ReleaseComponent(controller);
    }

    private readonly IKernel _kernel;

    private IKernel Kernel
    {
        get { return _kernel; }
    }
}

MvcApplication class 的 Application_Start 方法中添加以下内容:

var container = new WindsorContainer();

container.Install(FromAssembly.This());

ControllerBuilder.Current.SetControllerFactory(
    new WindsorControllerFactory(container.Kernel)
);

这应该可以与您现有的安装程序一起使用,并让您到达 Windsor 开始为您解决依赖关系的地步。您可能需要填补一些空白,但您会明白这一点的。

我大量借用了:https://github.com/castleproject/Windsor/blob/master/docs/mvc-tutorial-intro.md

谨慎使用 IDependencyResolver,因为它不会发布已解决的问题。

这里有几个选项:

1。使用 LifeStylePerWebRequest()UsingFactoryMethod()

首先,您可以将 OperationWrapper 注册为 LifestylePerWebRequest() 并将其注入到 BaseControllerScenarioService 中。 Windsor 将允许您使用用于创建它的工厂方法注册依赖项,该方法可以依次调用已注册的其他服务。

container.Register(Component.For<OperationWrapper>()
                            .LifestylePerWebRequest()
                            .UsingFactoryMethod(kernel =>
                            {
                               var userService = kernel.Resolve<IUserService>();
                               try
                               {
                                  return userService.GetOperations(
                                               HttpContext.Current.User.Identity.Name);
                               }
                               finally
                               {
                                  kernel.ReleaseComponent(userService);
                               }
                            }));

因此,每次 Windsor 被要求 OperationWrapper 时,它都会 运行 调用一个实例 if IUserService,给它当前的 Name User。通过将生活方式绑定到 LifestylePerWebRequest(),您可以验证每个请求都将获得其自己的 OperationWrapper 实例,并且不会跨请求流血。

(您 运行 遇到的唯一边缘情况是用户在请求中途通过身份验证,因此需要调整 OperationWrapper。如果这是一个正常路径用例,这可能需要重新考虑。)

然后,修改您的基本控制器以将该注册对象作为依赖项:

public abstract class BaseController : Controller {
    protected IUserService userService;
    protected OperationWrapper operations;
    public BaseController(IUserService userService, OperationWrapper operations) {
        this.userService = userService;
        this.operations = operations;
    }
}

2。使用方法注入

看起来 OperationWrapper 是某种上下文对象,有时可以将它们注入方法而不是构造函数。

例如,如果您的方法是:

int GetTransactionId() { /* use OperationWrapper property */ }

您可以将签名修改为:

int GetTransactionId(OperationWrapper operations) { /* use arg */ }

在这种情况下,如果您的服务方法的一小部分使用该依赖项,则使用它是有意义的。如果大多数(或全部)方法需要它,那么您可能应该走不同的路线。

3。根本不要对 OperationWrapper 使用 DI

在你有一个高度有状态的上下文对象的情况下(看起来你的 OperationWrapper 是),通常有一个 属性 的值被传递是有意义的。由于该对象基于某些当前线程状态,并且可以从任何子类中的任何地方访问 Controller,因此可能正确 只保留您拥有的模式。

如果您无法用 "use the pattern/container," 以外的任何其他方式回答问题 "What am I unable to do with OperationWrapper now that DI is going to solve for me?",这可能是针对这种特殊情况的选择。