HtmlHelper扩展方法中的依赖注入?

时间:2017-06-01 13:57:39

标签: asp.net-mvc dependency-injection asp.net-mvc-5 autofac

我想将属性渲染器实现为处理程序。我在应用程序中使用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);
    }
}

1 个答案:

答案 0 :(得分:2)

AFAIK,MVC 5并没有提供这样做的方法。但这并不意味着您可以连接自己的解决方案。

  

MVC Core现在使用对DI友好的视图组件,因此您不必跳过这么多箍。

根据文章DI Friendly Framework by Mark Seemann,您可以为HTML帮助程序创建一个工厂界面,该界面可用于使用其依赖项实例化它。

DefaultRendererFactory

首先,有一个默认工厂提供逻辑默认行为(无论是什么)。

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容器的情况下更灵活。

IRenderer

然后我们使用RendererIRenderer的抽象,以便可以轻松交换和/或通过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。如果您尝试概括,则基本上回到服务定位器。

AutofacRendererFactory

现在所有的部分都已到位,这就是你将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);