有时我们需要故意用错误来求得真相。
有一个控制器如下:
public class HomeController : Controller
{
//
// GET: /Home/
public HomeController(int i)
{
}
public ActionResult Index()
{
return View();
}
}
屏蔽了无参数的构造函数,在Application_Start中同时屏蔽NopDependencyResolver:
protected void Application_Start()
{
EngineContext.Initialize(false);
//set dependency resolver
var dependencyResolver = new NopDependencyResolver();
//DependencyResolver.SetResolver(dependencyResolver);
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
访问这个action,错误是:
放开NopDependencyResolver,错误是:
可以看到屏蔽NopDependencyResolver时,交给
System.Activator.CreateInstance
去创建控制器实例
放开时,交给
Nop.Web.Framework.Mvc.NopDependencyResolver.GetService
去创建控制器实例
通过对错误提示的分析,放开NopDependencyResolver时,纠结在参数不能解析,于是想到给控制器构造函数参数给默认值,果然就通过了,不再有错误。
public class HomeController : Controller
{
//
// GET: /Home/
public HomeController(int i=1)
{
}
public ActionResult Index()
{
return View();
}
}
切换成屏蔽NopDependencyResolver时,错误依然。
然后,还有然后,在网上,根据这个网址:
http://www.nopchina.com/forum.php?mod=viewthread&tid=109
又思考了一下,既然是ioc,那么去掉默认值,把int的实例注册进去应该是可以的,也就是这样:
去掉默认值:
public class HomeController : Controller
{
//
// GET: /Home/
public HomeController(int i)
{
}
public ActionResult Index()
{
return View();
}
}
注册一个int 单例:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
EngineContext.Initialize(false);
//set dependency resolver
var dependencyResolver = new NopDependencyResolver();
DependencyResolver.SetResolver(dependencyResolver);
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
EngineContext.Current.ContainerManager.AddComponentInstance<int>(100, "forhome", Nop.Core.Infrastructure.DependencyManagement.ComponentLifeStyle.Singleton);
}
}
结果是没有错误。
同时好像发现对int这种注册,必须是ComponentLifeStyle.Singleton,改成ComponentLifeStyle.Transient报错如下:
可以看见很多流程性的东西,至于100为什么必须是单例,也许可以纠结下去,但我还是打住。上面的图贴出来,就是为了看看调用Application_Start的流程而已。
然后想到,我注册多个int,效果是怎样的?
于是:
控制器:
public class HomeController : Controller
{
//
// GET: /Home/
private int n;
public HomeController(int i)
{
n = i;
}
public ActionResult Index()
{
ViewBag.n = n;
return View();
}
}
view:
@{
ViewBag.Title = "Index";
}
<h2>@ViewBag.n</h2>
Application_Start:
protected void Application_Start()
{
EngineContext.Initialize(false);
//set dependency resolver
var dependencyResolver = new NopDependencyResolver();
DependencyResolver.SetResolver(dependencyResolver);
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
EngineContext.Current.ContainerManager.AddComponentInstance<int>(100, "forhome1", Nop.Core.Infrastructure.DependencyManagement.ComponentLifeStyle.Singleton);
EngineContext.Current.ContainerManager.AddComponentInstance<int>(999, "forhome2", Nop.Core.Infrastructure.DependencyManagement.ComponentLifeStyle.Singleton);
EngineContext.Current.ContainerManager.AddComponentInstance<int>(777, "forhome3", Nop.Core.Infrastructure.DependencyManagement.ComponentLifeStyle.Singleton);
}
我猜是777,结果是它。
然后,又想到是否可以在:
protected void Application_BeginRequest(object sender, EventArgs e)
{
EngineContext.Current.ContainerManager.AddComponentInstance<HomeController>(new HomeController(2000), "ddd", Nop.Core.Infrastructure.DependencyManagement.ComponentLifeStyle.Singleton);
}
果然是2000,只是和上面一样,为什么必须ComponentLifeStyle.Singleton。
不是话:
为什么必须是单例?
回家后又仔细思考,首先我想清楚了为什么必须是ComponentLifeStyle.Singleton。因为AddComponentInstance注册进ioc容器中的本身就是一个实例,不是容器创建出来的,这样的实例,容器是不应该(不是不能)管理它的生命周期的。
想要一个临时的实例,应该使用注册基类(或接口)和实现类的方法,这样容器依据实现类创建的实例才可以指定为单例或者临时,这种创建出的实例显然只有通过容器才能拿到,因此它的生命周期完全由容器控制是合理的。
例子如下:
protected void Application_BeginRequest(object sender, EventArgs e)
{
EngineContext.Current.ContainerManager.AddComponentInstance<int>(2400, "p1");
Dictionary<string,string> p = new Dictionary<string,string>();
p.Add("i", "p1");// i是HomeController构造函数参数名
EngineContext.Current.ContainerManager.AddComponentWithParameters<Controller, HomeController>(p, "kkk", ComponentLifeStyle.Transient);
}
结果是2400。
protected void Application_BeginRequest(object sender, EventArgs e)
{
EngineContext.Current.ContainerManager.AddComponentInstance<int>(2500, "p1");
EngineContext.Current.ContainerManager.AddComponent<Controller, HomeController>( "kkk", ComponentLifeStyle.Transient);
}
是2500。
其实下面一句是多余的,改成如下也是可以的:
protected void Application_BeginRequest(object sender, EventArgs e)
{
EngineContext.Current.ContainerManager.AddComponentInstance<int>(2500, "p1");
}
还有一个很重要的发现:
NopDependencyResolver 与
PresentationNop.Web.FrameworkDependencyRegistrar.cs
中的这个注册是息息相关的:
//controllers
builder.RegisterControllers(typeFinder.GetAssemblies().ToArray());
如果不注册,会使用默认方式生成控制器
我写这么多,觉得收获主要有:
1、DependencyResolver.SetResolver :让我们有了干涉Controller实例生成的钩子,还可干涉别的啥,暂时不知道
2、NopDependencyResolver :让我们可以在Autofac的ioc容器中去发挥对Controller的生成控制
3、进一步理解了ioc