Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using IHtmlHelper with Dependency Injection

I would create a controller that response with the third-party IHtmlContent created through IHtmlHelper interface without using View() method. Actually I'm using a workaround:

I have created an extension function of IHtmlHelper that behaves like a "master" and other static function that behaves like "slaves". The master function call slaves based on data structure that It has received as a parameter. Slaves create the IHtmlContent based on third-party library. When all slave functions are completed, the controller send back the response. This behaviour is inside cshtml view called by controller.

Is possible to have IHtmlHelper as controller constructor parameter using dependency Injection?

Something like

public class MyTestController 
{
    private readonly IHtmlHelper _html;

    public MyTestController(IHtmlHelper html) {
     _html = html;
    }
}

If it were possible I would simplify many operations because I'm using this Master/Slave functions to replicate the behaviours of a class

Here you can find a pure example of how my code works now and how I would like to change it https://gist.github.com/Blackleones/eb0d02b9dd99164271af88e22143d72b#file-example-cs

I need this component because the third-party library is an extension of IHtmlHelper

like image 785
Leonardo Lurci Avatar asked Oct 26 '25 06:10

Leonardo Lurci


2 Answers

Short answer : Yes .


If you look into the source code of MVC, you will find that the IHtmlHelper is registered as a transient service :

    internal static void AddViewServices(IServiceCollection services)
    {

        ...

        //
        // HTML Helper
        //
        services.TryAddTransient<IHtmlHelper, HtmlHelper>();
        services.TryAddTransient(typeof(IHtmlHelper<>), typeof(HtmlHelper<>));
        services.TryAddSingleton<IHtmlGenerator, DefaultHtmlGenerator>();
        services.TryAddSingleton<ModelExpressionProvider>();
        // ModelExpressionProvider caches results. Ensure that it's re-used when the requested type is IModelExpressionProvider.
        services.TryAddSingleton<IModelExpressionProvider>(s => s.GetRequiredService<ModelExpressionProvider>());
        services.TryAddSingleton<ValidationHtmlAttributeProvider, DefaultValidationHtmlAttributeProvider>();

        services.TryAddSingleton<IJsonHelper, DefaultJsonHelper>();

        ...

    }

In other words, you could inject the IHtmlHelper service as you like.

Just as a reminder, we should always prefer to using IHtmlHelper within View Layer.

like image 103
itminus Avatar answered Oct 29 '25 07:10

itminus


I thank Itminus for answering me.

However call IHtmlHelper on controller constructor it is not enough, you must contextualize IHtmlHelper before using it.

I want to share another solution that I found and solve my problem, maybe It can be useful for other users. Please see https://gist.github.com/Blackleones/eb0d02b9dd99164271af88e22143d72b#file-example-cs to remember what my goal is.

  1. To solve my problem I have created a class called HtmlHelperAdapter that contains some instances I need.
public class MyHtmlHelperAdapter
{
        public IHtmlHelper _html;

        public NubessHtmlHelperAdapter(IHtmlHelper html)
        {
            _html = html;
        }

        public IHtmlHelper Html => _html;
        public ViewContext ViewContext => _html.ViewContext;
        public HttpContext HttpContext => _html.ViewContext.HttpContext;
        public ViewDataDictionary ViewData => _html.ViewData;
        public IServiceProvider provider => _html.ViewContext.HttpContext.RequestServices;
}
  1. after that I have created an extension method for IHtmlHelper
public static class MyBuilderExtension
{
        public static MyBuilder Nubess(this IHtmlHelper html)
        {
            return new MyBuilder(new MyHtmlHelperAdapter(html));
        }
}

where MyBuilder is

public class MyBuilder
    {
        private readonly MyHtmlHelperAdapter _htmlHelper;

        public MyBuilder(MyHtmlHelperAdapter htmlHelper)
        {
            _htmlHelper = htmlHelper;
        }

        public FormBuilder<object> Form<T>(IDictionary<string, string> customizeArgs = null, FormBuilder<object> externalForm = null)
        {
            return new FormBuilder(_htmlHelper, typeof(T), customizeArgs, externalForm).Build();
        }
    }
  1. now I can create my custom contents for views using class thanks to MyHtmlHelperAdapter
 public class FormBuilder
 {
        private MyHtmlHelperAdapter _htmlHelper;
        private readonly IStringLocalizer<SharedResources> _localizer;
        private readonly LinkGenerator _linkGenerator;
        private readonly IEngine _engine;
        private readonly IEngineElement _element;
        private readonly Type _typeDescriptor;
        private readonly IDictionary<string, string> _descriptorArgs;

        /* variabili per semplificare la gestione del builder */
        private readonly FormBuilder<object> Form;
        private readonly FormConfig Config;
        private readonly IList<FormGroupConfig> Groups;
        private readonly IList<FormItemConfig> Items;
        private readonly IDictionary<string, string> FormOptions;

        private readonly string _clientPrefix = "smtForm_{0}_";

        public FormBuilder(MyHtmlHelperAdapter htmlHelper, Type typeDescriptor, IDictionary<string, string> ModelCustomizeArgs = null, FormBuilder<object> externalForm = null)
        {
            _htmlHelper = htmlHelper;
            _localizer = _htmlHelper.provider.GetRequiredService<IStringLocalizer<SharedResources>>() ?? throw new ArgumentNullException();
            _linkGenerator = _htmlHelper.provider.GetRequiredService<LinkGenerator>() ?? throw new ArgumentNullException();
            _engine = _htmlHelper.provider.GetRequiredService<IEngine>() ?? throw new ArgumentNullException();

            //code..
        }


        public FormBuilder<object> Build()
        {
            //code..
            return Form;
        }

     //methods..
}
like image 35
Leonardo Lurci Avatar answered Oct 29 '25 05:10

Leonardo Lurci



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!