Dependency Injection & IViewLocationExpander (MVC)

huangapple go评论122阅读模式
英文:

Dependency Injection & IViewLocationExpander (MVC)

问题

I am trying to expand a singular branded website to support multiple different brands using the same code base.

A service has been created which retrieves the brand settings corresponding with the brand name found in the URL.

I want to set up the IViewLocation expander and inject into it this new brand retrieving service.

Like so:

public class ClientViewLocationExpander : IViewLocationExpander {
    private static readonly IBrandSettingsRetriever<BrandSettingsEntity> _brandSettings;
    public ClientViewLocationExpander(IBrandSettingsRetriever<BrandSettingsEntity> brandSettings) {
        _brandSettings = brandSettings;
    }
}

On:

IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations) {
    ...
}

I want to be able to use _brandSettings to get the name of the current brand in use and put that brand name into the location list. So when BrandA is in use, Razor looks for the BrandA home page.

Views/Home/{BrandA}/index.cshtml
Views/Home/{BrandB}/index.cshtml
Views/Home/{BrandC}/index.cshtml

The Issue:

services.Configure<RazorViewEngineOptions>(options => {
    options.ViewLocationExpanders.Add(new ClientViewLocationExpander());
});

This part of the IViewRenderService will complain if you try to use Dependency Injection on the IViewRenderService implementation. I have had a dig around the web looking for tricks or workarounds to get what I am trying to work.

I am rather, missing something obvious, or what I am trying to achieve simply isn't attainable.

Thanks for any help provided.

英文:

I am trying to expand a sigular branded website to support multiple different brands using the same code base.

A service has been created which retrieves the brand settings corrispoinding with the brand name found in the URL.

I want to set up the IViewLocation expander and inject into it this new brand retrieving service.

Like so:

public class ClientViewLocationExpander : IViewLocationExpander {
    private static readonly IBrandSettingsRetreiver&lt;BrandSettingsEntity&gt; _brandSettings;
    public ClientViewLocationExpander(IBrandSettingsRetreiver&lt;BrandSettingsEntity&gt; brandSettings) {
        _brandSettings = brandSettings
    }
}

On:

IEnumerable&lt;string&gt; ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable&lt;string&gt; viewLocations) {
    ...
}

I want to be able to use _brandSettings to get the name of the current brand in use, and put that brand name into the location list. So when BrandA is in use, Razor looks for the BrandA home page.

Views/Home/{BrandA}/index.cshtml
Views/Home/{BrandB}/index.cshtml
Views/Home/{BrandC}/index.cshtml

The Issue

    services.Configure&lt;RazorViewEngineOptions&gt;(options =&gt; {
        options.ViewLocationExpanders.Add(new ClientViewLocationExpander());
    });

This part of the IViewRenderService will complain if you try to use Dependency Injection on the IViewRenderService implementation. I have had a dig around the web looking for tricks or workarounds to get what I am trying to work.

I am rather, missing something obvious, or what I am trying to achieve simply isn't attainable.

Thanks for any help provided.

答案1

得分: 1

以下是您要翻译的部分:

"I recently try to implement custom location expander and come with the similar requirement.

You don't have to use constructor DI pattern for everything instead you can access IBrandSettingsRetreiver&lt;BrandSettingsEntity&gt; via HttpContext.RequestService from ViewLocationExpanderContext

    public IEnumerable&lt;string&gt; ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable&lt;string&gt; viewLocations)
    {
      var brandSetting = context.ActionContext.HttpContext.RequestServices.GetInstance&lt;BrandSettingsRetreiver&lt;BrandSettingsEntity&gt;&gt;();
    }

In addition, I would suggest you get name of brand from PopulateValues method instead of ExpandViewLocations method and pass brand via context.Values into ExpandViewLocations.

    public void PopulateValues(ViewLocationExpanderContext context)
    {
        var brandSetting = context.ActionContext.HttpContext.RequestServices.GetInstance&lt;BrandSettingsRetreiver&lt;BrandSettingsEntity&gt;&gt;();

        context.Values[&quot;brand&quot;] = brandSetting.GetBrandName();
    }

   public IEnumerable&lt;string&gt; ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable&lt;string&gt; viewLocations)
    {
       var brand = context.Values[&quot;brand&quot;];
       if(!string.IsNullOrWhiteSpace(brand))
       {
          // do your business here
       }
    }

I hope this helps."

英文:

I recently try to implement custom location expander and come with the similar requirement.

You don't have to use constructor DI pattern for everything instead you can access IBrandSettingsRetreiver&lt;BrandSettingsEntity&gt; via HttpContext.RequestService from ViewLocationExpanderContext

    public IEnumerable&lt;string&gt; ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable&lt;string&gt; viewLocations)
    {
      var brandSetting = context.ActionContext.HttpContext.RequestServices.GetInstance&lt;BrandSettingsRetreiver&lt;BrandSettingsEntity&gt;&gt;();
    }

In addition, I would suggest you get name of brand from PopulateValues method instead of ExpandViewLocations method and pass brand via context.Values into ExpandViewLocations.

    public void PopulateValues(ViewLocationExpanderContext context)
    {
        var brandSetting = context.ActionContext.HttpContext.RequestServices.GetInstance&lt;BrandSettingsRetreiver&lt;BrandSettingsEntity&gt;&gt;();

        context.Values[&quot;brand&quot;] = brandSetting.GetBrandName();
    }

   public IEnumerable&lt;string&gt; ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable&lt;string&gt; viewLocations)
    {
       var brand = context.Values[&quot;brand&quot;];
       if(!string.IsNullOrWhiteSpace(brand))
       {
          // do your business here
       }
    }

I hope this helps.

答案2

得分: 0

或许有更好的方法来做这个,但我成功找到了一种对我有效的方法,我打算称之为“手动注入”。

基本上,我在构造函数参数中设置了 IViewLocationExpander 与 IBrandSettingsRetreiver,就像普通的 DI 设置一样。

在启动时,我做了如下操作:

var brandGetter = services.BuildServiceProvider().GetRequiredService<IBrandSettingsRetreiver<BrandSettingsEntity>>();
services.AddScoped<IViewRenderService, ViewRenderService>();
services.Configure<RazorViewEngineOptions>(options => {
    options.ViewLocationExpanders.Add(new ClientViewLocationExpander(brandGetter));
});

目前,这似乎让我的服务按照我希望的方式运行。

希望能对你有所帮助。

英文:

Perhaps people will have better ways of doing this, but I managed to find a method which worked for me, which I am just going to call the 'manual injection'.

Basically, I set up the IViewLocationExpander with the IBrandSettingsRetreiver in the constructor parameters like a normal DI setup.

Where it complains in the startup, I did this:

    var brandGetter = services.BuildServiceProvider().GetRequiredService&lt;IBrandSettingsRetreiver&lt;BrandSettingsEntity&gt;&gt;();
    services.AddScoped&lt;IViewRenderService, ViewRenderService&gt;();
    services.Configure&lt;RazorViewEngineOptions&gt;(options =&gt; {
                options.ViewLocationExpanders.Add(new ClientViewLocationExpander(brandGetter));
    });

For now, this seems to be getting my services to do what I want them too.

Hope it helps.

huangapple
  • 本文由 发表于 2023年3月9日 18:05:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/75683074.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定