HtmlHelper扩展方法中的依赖注入? [英] Dependency Injection in HtmlHelper extension method?

查看:62
本文介绍了HtmlHelper扩展方法中的依赖注入?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想将属性渲染器实现为处理程序.我在应用程序中将Autofac用作DI容器.如何在不使用可全局访问的容器(服务位置)的情况下获取在HtmlHelper扩展中实现IPropertyHandler的对象?这是在Autofac中注册自己的HtmlHelper的方法吗?也许MVC框架提供了另一种方法?

I want to implement property renderers as handlers. I am using Autofac as a DI container in the app. How can I get objects implementing IPropertyHandler in HtmlHelper extension without using globally accessible container (service location)? Is it a way to register own HtmlHelper in Autofac? Maybe MVC framework provide another way?

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);
    }
}

推荐答案

AFAIK,MVC 5没有提供实现此目的的方法.但这并不意味着您无法连接自己的解决方案.

AFAIK, MVC 5 doesn't provide a way to do this. But that doesn't mean you can't wire up your own solution.

MVC Core现在使用DI友好的 view组件,因此您不必经历那么多的麻烦.

MVC Core now uses view components that are DI friendly, so you don't have to jump through so many hoops.

根据文章 Mark Seemann的DI友好框架,您可以为HTML帮助器创建一个工厂界面,该界面可用于通过其依赖关系实例化它.

As per the article DI Friendly Framework by Mark Seemann, you can make a factory interface for your HTML helper that can be used to instantiate it with its dependencies.

首先,有一个默认工厂提供逻辑默认行为(无论是哪种行为).

First there is a default factory that provides the logical default behavior (whatever that is).

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();
        }
    }
}

您可能希望使此默认工厂更智能,甚至根据其他文章

You may wish to make this default factory smarter or even use a fluent builder to supply its dependencies as per the other article DI Friendly Library so it is more flexible without using a DI container.

然后,我们对RendererIRenderer使用抽象,以便可以轻松交换和/或通过DI提供.

Then we use an abstraction for Renderer, IRenderer so it can be swapped easily and/or provided via 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帮助程序是静态扩展方法,因此唯一的选择是使用静态属性或方法设置静态字段以对其进行设置.如果需要在工厂中使用装饰器图案,那么做吸气剂始终是一种很好的做法.

Factory Registration

Next, we provide a hook to register the factory. Since the HTML helper is a static extension method, the only option is to make a static field with a static property or method to set it. Its always good practice to make a getter as well in case there is a need to use a decorator pattern on the factory.

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.如果您尝试概括,那么您基本上会回到服务定位器.

You could provide some logical place to register all of your factories statically, if that makes more sense for the app. But there will basically need to be a factory per HTML helper to adhere to the SRP. If you try to generalize, you are basically back to a service locator.

现在所有零件都准备就绪,这就是将Autofac纳入方程式的方式.您将需要一个自定义的IRendererFactory,它将成为Autofac特有的合成根的一部分.

Now that all of the pieces are in place, this is how you would slip Autofac into the equation. You will need a custom IRendererFactory that you will make part of your composition root that is specific to 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.

Next, you need to add the type mappings for IRenderer and its dependencies to Autofac.

最后但并非最不重要的一点是,在创建Autofac容器以在应用程序需要渲染器时解析渲染器之后,您需要在应用程序启动时添加一行.

Last but not least, you will need to add a line to your application startup after creating the Autofac container to resolve the renderer when it is needed by the application.

// Register all of your types with the builder
// ...
// ...
Autofac.IContainer container = builder.Build();
HtmlHelperExtensions.RendererFactory = new AutofacRendererFactory(container);

这篇关于HtmlHelper扩展方法中的依赖注入?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆