我有一个自定义的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;
}
}
这是我的Windsor安装程序。
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做过类似的事情。我需要将哪些内容添加到安装程序才能使其正常工作?它甚至可能吗?
答案 0 :(得分:1)
这里有一些选择:
LifeStylePerWebRequest()
和UsingFactoryMethod()
首先,您可以将OperationWrapper
注册为LifestylePerWebRequest()
并将其注入BaseController
和ScenarioService
。 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
时,它会针对IUserService
的实例运行该调用,并为其提供当前Name
的{{1}}。通过将生活方式绑定到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;
}
}
是某种上下文对象,有时可以将它们注入到方法而不是构造函数中。
例如,如果您的方法是:
OperationWrapper
您可以将签名修改为:
int GetTransactionId() { /* use OperationWrapper property */ }
在这种情况下,如果服务方法的一小部分使用该依赖关系,则使用它是有意义的。如果大多数(或整体)方法需要它,那么你应该采用不同的方法。
int GetTransactionId(OperationWrapper operations) { /* use arg */ }
在你有一个高状态的上下文对象(看起来你的OperationWrapper
是)的情况下,拥有一个值传递的属性通常是有意义的。由于该对象基于某些当前线程状态,并且可以从任何子类OperationWrapper
中的任何位置访问,因此它可能正确以保持您拥有的模式。
如果你不能回答“我现在无法用Controller
做什么,DI会为我解决的问题?”除了“使用模式/容器”之外的任何东西,这可能是这种特殊情况的选择。
答案 1 :(得分:0)
您应该在global.asax
的Application_Start方法中设置依赖项解析器System.Web.MVC.DependencyResolver.SetResolver(your windsor resolver)
答案 2 :(得分:0)
创建一个继承自DefaultControllerFactory
的类。这样的事情会做:
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; }
}
}
在Application_Start
课程的MvcApplication
方法中添加以下内容:
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,因为它没有提供释放已解决的内容。