使用 Unity 的 Sitecore DI
Sitecore DI with Unity
我正在尝试建立一个全新的 Sitecore 7.2 网站,我希望将 MVC 5、Glass Mapper 和 Microsoft Unity 集成为一个 DI 容器,而 Sitecore 不想玩得很好。
我的场景是这样的:
- 有一个名为 PoC.Quotes.Web 的 Web 项目 - 这将仅包含 CSS、HTML 和任何其他资产,没有控制器
- 获得了一个名为 PoC.Quotes.Controllers 的 class 库项目 - 它仅包含控制器
- 获得了一个名为 PoC.Quotes.DataLayer 的 class 库项目 - 它包含一个接口 ISitecoreRepository 及其具体实现 SitecoreRepository
SitecoreRepository class 有一个接收 1 个参数的构造函数,Glass Mapper 上下文,我的控制器之一在构造函数中接收 1 个参数...ISitecoreRepository。
Sitecore 存储库 class:
public class SitecoreRepository : ISitecoreRepository
{
ISitecoreContext sitecoreContext = null;
public SitecoreRepository(ISitecoreContext context)
{
this.sitecoreContext = context;
}
}
控制器class:
public class HomeController : Controller
{
private ISitecoreRepository _repository;
public HomeController(ISitecoreRepository repository)
{
this._repository = repository;
}
}
每次我 运行 Sitecore 项目都会抛出一个错误,指出它无法创建类型为 (PoC.Quotes.Controllers.HomeController, PoC.Quotes.Controllers) 的控制器。我猜它显示的是完全限定名称,因为这是我在控制器渲染中设置它的方式。
第一个问题是控制器构造函数参数。我把它拿出来并使用这个语句来获取存储库的实例:
System.Web.Mvc.DependencyResolver.Current.GetService<ISitecoreRepository>();
结果为空,因为 class SitecoreRepository 只有 1 个构造函数和 1 个参数,并且不会被实例化。一旦我也排除了该参数的问题,那么一切都很好。
但是,对我来说,这有点违背了拥有 DI 容器的目的。
我试过查看 Castle Windsor,但尽管网上有更多文档,但没有任何效果,因为我遇到了类似的问题。
这有点烦人,因为如果我 运行 在一个基本的 MVC 5 应用程序中进行类似的测试(我这样做只是为了确保我不会发疯),在不到 5 的时间内一切正常分钟。
有什么想法吗?
编辑:
有趣的是,在这个问题上花了几个小时后,我注意到实际上 Unity 或 Windsor 容器都可以正常工作,但有一个限制......一个很大的限制。
在我的控制器渲染中,我将控制器 属性 设置为控制器的完全限定名称:
PoC.Quotes.Controllers.HomeController, PoC.Quotes.Controllers
但是,如果我进入 Sitecore 并将 属性 更改为“主页”,神奇的是一切都很好。我什至尝试过使用 PoC.Quotes.Controllers.Home 的临时版本,但仍然出现错误,请注意另一个错误。
不确定我是否做错了什么,但感觉有点奇怪。
有什么办法解决这个问题吗?
虽然我不知道您的注册是如何配置的,但听起来您最好使用控制器工厂。示例是 Windsor,但您可以在 Unity 中轻松交换。为了不修改 Global.asax,您还可以使用 WebActivatorEx 连接引导启动。
引导程序
[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(Site.Website.Cms.WindsorConfig), "RegisterComponents")]`
[assembly: WebActivatorEx.ApplicationShutdownMethod(typeof(Site.Website.Cms.WindsorConfig), "ReleaseComponents")]
/// <summary>
/// Provides a bootstrapping and resolving hook for dependency resolution for MVC, Web API, and service locator.
/// </summary>
public static class WindsorConfig
{
private static Lazy<IWindsorContainer> _container;
static WindsorConfig()
{
_container = new Lazy<IWindsorContainer>(() => BuildContainer());
}
public static IWindsorContainer WindsorContainer
{
get
{
return _container.Value;
}
}
/// <summary>
/// Generates and configures the container when the application is started.
/// </summary>
public static void RegisterComponents()
{
ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(WindsorContainer));
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new WindsorControllerActivator(WindsorContainer));
}
/// <summary>
/// Disposes of the container when the application is shut down.
/// </summary>
public static void ReleaseComponents()
{
WindsorContainer.Dispose();
}
}
控制器工厂
/// <summary>
/// Provides controller dependency resolving for ASP.NET MVC controllers.
/// </summary>
public class WindsorControllerFactory : DefaultControllerFactory
{
private readonly IWindsorContainer _container;
public WindsorControllerFactory(IWindsorContainer container)
{
if (container == null) throw new ArgumentNullException("container");
this._container = container;
}
public override void ReleaseController(IController controller)
{
this._container.Release(controller);
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", requestContext.HttpContext.Request.Path));
}
return (IController)this._container.Resolve(controllerType);
}
}
为此花费了大量时间并设法提出了解决方案。写到这里有点长,所以我把它放在博客上了post http://agooddayforscience.blogspot.co.uk/2015/10/sitecore-multi-tenancy-and-di-containers.html
基本上这不是 Unity 或其他 DI 容器的问题,而是关于 Sitecore 如何处理完全限定名称的问题。是的,我知道理想情况下您不想使用它们而是遵循 MVC 模式,但我在博客 post 中解释了为什么使用完全限定名称。
作为高级解释,问题出在 2 Sitecore 类 ControllerRunner 和 SitecoreControllerFactory 中。 类 都包含了一些识别完全限定名的方法,并使用反射调用无参数构造函数来实例化一个新的实例。我应用的修复程序覆盖了这些方法以调用控制器工厂。
感谢您提供的所有帮助。
安德烈
我正在尝试建立一个全新的 Sitecore 7.2 网站,我希望将 MVC 5、Glass Mapper 和 Microsoft Unity 集成为一个 DI 容器,而 Sitecore 不想玩得很好。
我的场景是这样的:
- 有一个名为 PoC.Quotes.Web 的 Web 项目 - 这将仅包含 CSS、HTML 和任何其他资产,没有控制器
- 获得了一个名为 PoC.Quotes.Controllers 的 class 库项目 - 它仅包含控制器
- 获得了一个名为 PoC.Quotes.DataLayer 的 class 库项目 - 它包含一个接口 ISitecoreRepository 及其具体实现 SitecoreRepository
SitecoreRepository class 有一个接收 1 个参数的构造函数,Glass Mapper 上下文,我的控制器之一在构造函数中接收 1 个参数...ISitecoreRepository。
Sitecore 存储库 class:
public class SitecoreRepository : ISitecoreRepository
{
ISitecoreContext sitecoreContext = null;
public SitecoreRepository(ISitecoreContext context)
{
this.sitecoreContext = context;
}
}
控制器class:
public class HomeController : Controller
{
private ISitecoreRepository _repository;
public HomeController(ISitecoreRepository repository)
{
this._repository = repository;
}
}
每次我 运行 Sitecore 项目都会抛出一个错误,指出它无法创建类型为 (PoC.Quotes.Controllers.HomeController, PoC.Quotes.Controllers) 的控制器。我猜它显示的是完全限定名称,因为这是我在控制器渲染中设置它的方式。
第一个问题是控制器构造函数参数。我把它拿出来并使用这个语句来获取存储库的实例:
System.Web.Mvc.DependencyResolver.Current.GetService<ISitecoreRepository>();
结果为空,因为 class SitecoreRepository 只有 1 个构造函数和 1 个参数,并且不会被实例化。一旦我也排除了该参数的问题,那么一切都很好。
但是,对我来说,这有点违背了拥有 DI 容器的目的。
我试过查看 Castle Windsor,但尽管网上有更多文档,但没有任何效果,因为我遇到了类似的问题。
这有点烦人,因为如果我 运行 在一个基本的 MVC 5 应用程序中进行类似的测试(我这样做只是为了确保我不会发疯),在不到 5 的时间内一切正常分钟。
有什么想法吗?
编辑:
有趣的是,在这个问题上花了几个小时后,我注意到实际上 Unity 或 Windsor 容器都可以正常工作,但有一个限制......一个很大的限制。
在我的控制器渲染中,我将控制器 属性 设置为控制器的完全限定名称:
PoC.Quotes.Controllers.HomeController, PoC.Quotes.Controllers
但是,如果我进入 Sitecore 并将 属性 更改为“主页”,神奇的是一切都很好。我什至尝试过使用 PoC.Quotes.Controllers.Home 的临时版本,但仍然出现错误,请注意另一个错误。
不确定我是否做错了什么,但感觉有点奇怪。
有什么办法解决这个问题吗?
虽然我不知道您的注册是如何配置的,但听起来您最好使用控制器工厂。示例是 Windsor,但您可以在 Unity 中轻松交换。为了不修改 Global.asax,您还可以使用 WebActivatorEx 连接引导启动。
引导程序
[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(Site.Website.Cms.WindsorConfig), "RegisterComponents")]`
[assembly: WebActivatorEx.ApplicationShutdownMethod(typeof(Site.Website.Cms.WindsorConfig), "ReleaseComponents")]
/// <summary>
/// Provides a bootstrapping and resolving hook for dependency resolution for MVC, Web API, and service locator.
/// </summary>
public static class WindsorConfig
{
private static Lazy<IWindsorContainer> _container;
static WindsorConfig()
{
_container = new Lazy<IWindsorContainer>(() => BuildContainer());
}
public static IWindsorContainer WindsorContainer
{
get
{
return _container.Value;
}
}
/// <summary>
/// Generates and configures the container when the application is started.
/// </summary>
public static void RegisterComponents()
{
ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(WindsorContainer));
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new WindsorControllerActivator(WindsorContainer));
}
/// <summary>
/// Disposes of the container when the application is shut down.
/// </summary>
public static void ReleaseComponents()
{
WindsorContainer.Dispose();
}
}
控制器工厂
/// <summary>
/// Provides controller dependency resolving for ASP.NET MVC controllers.
/// </summary>
public class WindsorControllerFactory : DefaultControllerFactory
{
private readonly IWindsorContainer _container;
public WindsorControllerFactory(IWindsorContainer container)
{
if (container == null) throw new ArgumentNullException("container");
this._container = container;
}
public override void ReleaseController(IController controller)
{
this._container.Release(controller);
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", requestContext.HttpContext.Request.Path));
}
return (IController)this._container.Resolve(controllerType);
}
}
为此花费了大量时间并设法提出了解决方案。写到这里有点长,所以我把它放在博客上了post http://agooddayforscience.blogspot.co.uk/2015/10/sitecore-multi-tenancy-and-di-containers.html
基本上这不是 Unity 或其他 DI 容器的问题,而是关于 Sitecore 如何处理完全限定名称的问题。是的,我知道理想情况下您不想使用它们而是遵循 MVC 模式,但我在博客 post 中解释了为什么使用完全限定名称。
作为高级解释,问题出在 2 Sitecore 类 ControllerRunner 和 SitecoreControllerFactory 中。 类 都包含了一些识别完全限定名的方法,并使用反射调用无参数构造函数来实例化一个新的实例。我应用的修复程序覆盖了这些方法以调用控制器工厂。
感谢您提供的所有帮助。
安德烈