我想将属性渲染器实现为处理程序。我在应用程序中使用Autofac作为DI容器。如何在不使用全局可访问容器(服务位置)的情况下获取在HtmlHelper扩展中实现IPropertyHandler的对象?这是在Autofac中注册自己的HtmlHelper的方法吗?也许MVC框架提供了另一种方式?
public static class HtmlHelperExtensions {
public static MvcHtmlString Editor(this HtmlHelper html, object model) {
return new Renderer(new List<IPropertyHandler>() /*Where to get these objects?*/ ).Render(html, model);
}
}
public class Renderer {
private readonly ICollection<IPropertyHandler> _propertyRenderers;
public Renderer(ICollection<IPropertyHandler> propertyRenderers) {
_propertyRenderers = propertyRenderers;
}
public MvcHtmlString Render(HtmlHelper html, object model) {
var result = "";
foreach(var prop in model.GetType().GetProperties()) {
var renderers = _propertyRenderers.OrderBy(b => b.Order);
//impl
}
return new MvcHtmlString(result);
}
}
答案 0 :(得分:2)
AFAIK,MVC 5并没有提供这样做的方法。但这并不意味着您可以连接自己的解决方案。
MVC Core现在使用对DI友好的视图组件,因此您不必跳过这么多箍。
根据文章DI Friendly Framework by Mark Seemann,您可以为HTML帮助程序创建一个工厂界面,该界面可用于使用其依赖项实例化它。
首先,有一个默认工厂提供逻辑默认行为(无论是什么)。
public interface IRendererFactory
{
IRenderer Create();
void Release(IRenderer renderer);
}
public class DefaultRendererFactory : IRendererFactory
{
public virtual IRenderer Create()
{
return new Renderer(new IPropertyHandler[] { new DefaultPropertyHandler1(), DefaultPropertyHandler2() });
}
public virtual void Release(IRenderer renderer)
{
var disposable = renderer as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}
}
您可能希望使此默认工厂更智能,或者甚至使用流畅的构建器来提供其依赖项,而不是使用其他文章DI Friendly Library,因此在不使用DI容器的情况下更灵活。
然后我们使用Renderer
,IRenderer
的抽象,以便可以轻松交换和/或通过DI提供。
public interface IRenderer
{
MvcHtmlString Render(HtmlHelper html, object model);
}
public class Renderer : IRenderer
{
private readonly ICollection<IPropertyHandler> _propertyRenderers;
public Renderer(ICollection<IPropertyHandler> propertyRenderers)
{
_propertyRenderers = propertyRenderers;
}
public MvcHtmlString Render(HtmlHelper html, object model)
{
var result = "";
foreach(var prop in model.GetType().GetProperties())
{
var renderers = _propertyRenderers.OrderBy(b => b.Order);
//impl
}
return new MvcHtmlString(result);
}
}
接下来,我们提供一个挂钩来注册工厂。由于HTML帮助器是静态扩展方法,因此唯一的选择是使用静态属性或方法创建静态字段来设置它。如果需要在工厂使用装饰器图案,那么制作吸气剂总是很好的做法。
public interface IRendererFactory
{
IRenderer Create();
void Release(IRenderer renderer);
}
public static class HtmlHelperExtensions {
private static IRendererFactory rendererFactory = new DefaultRendererFactory();
public static IRendererFactory RendererFactory
{
get { return rendererFactory; }
set { rendererFactory = value; }
}
public static MvcHtmlString Editor(this HtmlHelper html, object model) {
var renderer = rendererFactory.Create();
try
{
return renderer.Render(html, model);
}
finally
{
rendererFactory.Release(renderer);
}
}
}
如果对应用程序更有意义,您可以提供一些逻辑位置来静态注册所有工厂。但是,每个HTML帮助程序基本上都需要一个工厂才能遵守SRP。如果您尝试概括,则基本上回到服务定位器。
现在所有的部分都已到位,这就是你将Autofac放到等式中的方法。您需要一个自定义IRendererFactory
,您将作为Autofac特有的合成根的一部分。
public class AutofacRendererFactory : IRendererFactory
{
private readonly Autofac.IContainer container;
public AutofacRendererFactory(Autofac.IContainer container)
{
if (container == null)
throw new ArgumentNullException("container");
this.container = container;
}
public IRenderer Create()
{
return this.container.Resolve(typeof(IRenderer));
}
public void Release(IRenderer renderer)
{
// allow autofac to release dependencies using lifetime management
}
}
接下来,您需要将IRenderer
及其依赖项的类型映射添加到Autofac。
最后但并非最不重要的一点是,在创建Autofac容器以在应用程序需要时解析渲染器时,您需要在应用程序启动时添加一行。
// Register all of your types with the builder
// ...
// ...
Autofac.IContainer container = builder.Build();
HtmlHelperExtensions.RendererFactory = new AutofacRendererFactory(container);