TagHelper for class inlining
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
1
down vote
favorite
I'm building a RESTful service that sends emails with body rendered by partial views. It replaces my old solution that used hardcoded templates of IHtmlElement
inside each application.
One of its features is to inline class
es as style
. I'm using class
because it's easier to design a view this way. Since it's about emails, I'm not expecting any fency style selectors and to make it simple I'm using only class
es.
InlineClassTagHelper
The inlining is driven by the InlineClassTagHelper
. It stops at each element with the class
attribute and checks if there are classes prefixed with m-
(it's my custom prefix that stands for mail). It then looks for a style in the parsed .css
file. Its name is always wwwroot/css/Conroller.Action.css
. When found, it sets the style
attribute and removes the class
one.
[HtmlTargetElement(Attributes = "class")]
public class InlineClassTagHelper : TagHelper
private readonly CssProvider _cssProvider;
public InlineClassTagHelper(CssProvider cssProvider)
_cssProvider = cssProvider;
[HtmlAttributeNotBound, ViewContext]
public ViewContext ViewContext get; set;
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
var classNames =
output
.Attributes["class"]
?.Value
.ToString()
.Split(new ' ' , StringSplitOptions.RemoveEmptyEntries);
if (classNames is null)
return;
var inlineableClassNames =
(from className in classNames
where className.StartsWith("m-")
select SoftString.Create(className)).ToImmutableHashSet();
if (inlineableClassNames.None())
return;
var cssFileName =
$"wwwroot/css/" +
$"ViewContext.RouteData.Values["controller"]." +
$"ViewContext.RouteData.Values["action"].css";
var css = await _cssProvider.GetCss(cssFileName);
var declarations =
from ruleset in css
from selector in ruleset.Selectors
join className in inlineableClassNames on selector equals className
select ruleset.Declarations.TrimEnd(';');
var style = declarations.Join("; ");
if (style.IsNullOrEmpty())
// Make debugging of missing styles easier by highlighting the element with a red border.
output.Attributes.SetAttribute("style", "border: 1px solid #ff6666; border-radius: 3px;");
else
output.Attributes.SetAttribute("style", style);
output.Attributes.RemoveAll("class");
Caching
To avoid multiple loads (for each class
attribute occurance) the .css
file is cached for the lifetime of a request and loaded by another helper service I call ICssProvider
.
public interface ICssProvider
Task<Css> GetCss(string fileName);
public class CssProvider : ICssProvider
private readonly IFileProvider _fileProvider;
private Css _css;
public CssProvider(IFileProvider fileProvider)
_fileProvider = fileProvider;
public async Task<Css> GetCss(string fileName)
if (_css is null)
var cssFile = _fileProvider.GetFileInfo(fileName);
using (var reader = new StreamReader(cssFile.CreateReadStream()))
var cssString = await reader.ReadToEndAsync();
_css = CssParser.Default.Parse(cssString);
Debug.WriteLine($"fileName loaded.");
return _css;
This is registered in Startup
as
services.AddScoped<CssProvider>();
Example
When the tag-helper finds an element like this one:
<h2 class="m-title">@ViewData["Title"]</h2>
it turns it into this:
<h2 style="color: blueviolet;">About</h2>
where the .css
is:
m-title
color: blueviolet;
Would you say it's a good solution or do you see any room for improvement?
c# html css asp.net-core
 |Â
show 2 more comments
up vote
1
down vote
favorite
I'm building a RESTful service that sends emails with body rendered by partial views. It replaces my old solution that used hardcoded templates of IHtmlElement
inside each application.
One of its features is to inline class
es as style
. I'm using class
because it's easier to design a view this way. Since it's about emails, I'm not expecting any fency style selectors and to make it simple I'm using only class
es.
InlineClassTagHelper
The inlining is driven by the InlineClassTagHelper
. It stops at each element with the class
attribute and checks if there are classes prefixed with m-
(it's my custom prefix that stands for mail). It then looks for a style in the parsed .css
file. Its name is always wwwroot/css/Conroller.Action.css
. When found, it sets the style
attribute and removes the class
one.
[HtmlTargetElement(Attributes = "class")]
public class InlineClassTagHelper : TagHelper
private readonly CssProvider _cssProvider;
public InlineClassTagHelper(CssProvider cssProvider)
_cssProvider = cssProvider;
[HtmlAttributeNotBound, ViewContext]
public ViewContext ViewContext get; set;
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
var classNames =
output
.Attributes["class"]
?.Value
.ToString()
.Split(new ' ' , StringSplitOptions.RemoveEmptyEntries);
if (classNames is null)
return;
var inlineableClassNames =
(from className in classNames
where className.StartsWith("m-")
select SoftString.Create(className)).ToImmutableHashSet();
if (inlineableClassNames.None())
return;
var cssFileName =
$"wwwroot/css/" +
$"ViewContext.RouteData.Values["controller"]." +
$"ViewContext.RouteData.Values["action"].css";
var css = await _cssProvider.GetCss(cssFileName);
var declarations =
from ruleset in css
from selector in ruleset.Selectors
join className in inlineableClassNames on selector equals className
select ruleset.Declarations.TrimEnd(';');
var style = declarations.Join("; ");
if (style.IsNullOrEmpty())
// Make debugging of missing styles easier by highlighting the element with a red border.
output.Attributes.SetAttribute("style", "border: 1px solid #ff6666; border-radius: 3px;");
else
output.Attributes.SetAttribute("style", style);
output.Attributes.RemoveAll("class");
Caching
To avoid multiple loads (for each class
attribute occurance) the .css
file is cached for the lifetime of a request and loaded by another helper service I call ICssProvider
.
public interface ICssProvider
Task<Css> GetCss(string fileName);
public class CssProvider : ICssProvider
private readonly IFileProvider _fileProvider;
private Css _css;
public CssProvider(IFileProvider fileProvider)
_fileProvider = fileProvider;
public async Task<Css> GetCss(string fileName)
if (_css is null)
var cssFile = _fileProvider.GetFileInfo(fileName);
using (var reader = new StreamReader(cssFile.CreateReadStream()))
var cssString = await reader.ReadToEndAsync();
_css = CssParser.Default.Parse(cssString);
Debug.WriteLine($"fileName loaded.");
return _css;
This is registered in Startup
as
services.AddScoped<CssProvider>();
Example
When the tag-helper finds an element like this one:
<h2 class="m-title">@ViewData["Title"]</h2>
it turns it into this:
<h2 style="color: blueviolet;">About</h2>
where the .css
is:
m-title
color: blueviolet;
Would you say it's a good solution or do you see any room for improvement?
c# html css asp.net-core
1
Initial observation, assumingCssParser
is static (tight coupling to static dependencies), is there any way to abstract that out and have it explicitly injected via constructor?
â Nkosi
Feb 24 at 17:14
@Nkosi you're right, it should be injected. I actually have an interface for it, it's theICssParser
and I am able to do it correctly, but I also have thisDefault
property in case I'm too lazy for a full DI ;-)
â t3chb0t
Feb 24 at 18:46
The CSS provider can cache the filename mapped to the CSS in a dictionary. The current design uses the same one for all file names after the initial set.
â Nkosi
Feb 25 at 5:02
You also need to register the interface. You are only registering the implementation.
â Nkosi
Feb 25 at 5:06
@Nkosi I think this is ok because there is only one file per action and each action is one request, right? So for each request there will be only a single fine in the cache. That's what theScoped
service registration does. It is resolved one per request. And within a single request the tag-helper is created everytime it finds theclass
attribute but still this is the same request.
â t3chb0t
Feb 25 at 8:58
 |Â
show 2 more comments
up vote
1
down vote
favorite
up vote
1
down vote
favorite
I'm building a RESTful service that sends emails with body rendered by partial views. It replaces my old solution that used hardcoded templates of IHtmlElement
inside each application.
One of its features is to inline class
es as style
. I'm using class
because it's easier to design a view this way. Since it's about emails, I'm not expecting any fency style selectors and to make it simple I'm using only class
es.
InlineClassTagHelper
The inlining is driven by the InlineClassTagHelper
. It stops at each element with the class
attribute and checks if there are classes prefixed with m-
(it's my custom prefix that stands for mail). It then looks for a style in the parsed .css
file. Its name is always wwwroot/css/Conroller.Action.css
. When found, it sets the style
attribute and removes the class
one.
[HtmlTargetElement(Attributes = "class")]
public class InlineClassTagHelper : TagHelper
private readonly CssProvider _cssProvider;
public InlineClassTagHelper(CssProvider cssProvider)
_cssProvider = cssProvider;
[HtmlAttributeNotBound, ViewContext]
public ViewContext ViewContext get; set;
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
var classNames =
output
.Attributes["class"]
?.Value
.ToString()
.Split(new ' ' , StringSplitOptions.RemoveEmptyEntries);
if (classNames is null)
return;
var inlineableClassNames =
(from className in classNames
where className.StartsWith("m-")
select SoftString.Create(className)).ToImmutableHashSet();
if (inlineableClassNames.None())
return;
var cssFileName =
$"wwwroot/css/" +
$"ViewContext.RouteData.Values["controller"]." +
$"ViewContext.RouteData.Values["action"].css";
var css = await _cssProvider.GetCss(cssFileName);
var declarations =
from ruleset in css
from selector in ruleset.Selectors
join className in inlineableClassNames on selector equals className
select ruleset.Declarations.TrimEnd(';');
var style = declarations.Join("; ");
if (style.IsNullOrEmpty())
// Make debugging of missing styles easier by highlighting the element with a red border.
output.Attributes.SetAttribute("style", "border: 1px solid #ff6666; border-radius: 3px;");
else
output.Attributes.SetAttribute("style", style);
output.Attributes.RemoveAll("class");
Caching
To avoid multiple loads (for each class
attribute occurance) the .css
file is cached for the lifetime of a request and loaded by another helper service I call ICssProvider
.
public interface ICssProvider
Task<Css> GetCss(string fileName);
public class CssProvider : ICssProvider
private readonly IFileProvider _fileProvider;
private Css _css;
public CssProvider(IFileProvider fileProvider)
_fileProvider = fileProvider;
public async Task<Css> GetCss(string fileName)
if (_css is null)
var cssFile = _fileProvider.GetFileInfo(fileName);
using (var reader = new StreamReader(cssFile.CreateReadStream()))
var cssString = await reader.ReadToEndAsync();
_css = CssParser.Default.Parse(cssString);
Debug.WriteLine($"fileName loaded.");
return _css;
This is registered in Startup
as
services.AddScoped<CssProvider>();
Example
When the tag-helper finds an element like this one:
<h2 class="m-title">@ViewData["Title"]</h2>
it turns it into this:
<h2 style="color: blueviolet;">About</h2>
where the .css
is:
m-title
color: blueviolet;
Would you say it's a good solution or do you see any room for improvement?
c# html css asp.net-core
I'm building a RESTful service that sends emails with body rendered by partial views. It replaces my old solution that used hardcoded templates of IHtmlElement
inside each application.
One of its features is to inline class
es as style
. I'm using class
because it's easier to design a view this way. Since it's about emails, I'm not expecting any fency style selectors and to make it simple I'm using only class
es.
InlineClassTagHelper
The inlining is driven by the InlineClassTagHelper
. It stops at each element with the class
attribute and checks if there are classes prefixed with m-
(it's my custom prefix that stands for mail). It then looks for a style in the parsed .css
file. Its name is always wwwroot/css/Conroller.Action.css
. When found, it sets the style
attribute and removes the class
one.
[HtmlTargetElement(Attributes = "class")]
public class InlineClassTagHelper : TagHelper
private readonly CssProvider _cssProvider;
public InlineClassTagHelper(CssProvider cssProvider)
_cssProvider = cssProvider;
[HtmlAttributeNotBound, ViewContext]
public ViewContext ViewContext get; set;
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
var classNames =
output
.Attributes["class"]
?.Value
.ToString()
.Split(new ' ' , StringSplitOptions.RemoveEmptyEntries);
if (classNames is null)
return;
var inlineableClassNames =
(from className in classNames
where className.StartsWith("m-")
select SoftString.Create(className)).ToImmutableHashSet();
if (inlineableClassNames.None())
return;
var cssFileName =
$"wwwroot/css/" +
$"ViewContext.RouteData.Values["controller"]." +
$"ViewContext.RouteData.Values["action"].css";
var css = await _cssProvider.GetCss(cssFileName);
var declarations =
from ruleset in css
from selector in ruleset.Selectors
join className in inlineableClassNames on selector equals className
select ruleset.Declarations.TrimEnd(';');
var style = declarations.Join("; ");
if (style.IsNullOrEmpty())
// Make debugging of missing styles easier by highlighting the element with a red border.
output.Attributes.SetAttribute("style", "border: 1px solid #ff6666; border-radius: 3px;");
else
output.Attributes.SetAttribute("style", style);
output.Attributes.RemoveAll("class");
Caching
To avoid multiple loads (for each class
attribute occurance) the .css
file is cached for the lifetime of a request and loaded by another helper service I call ICssProvider
.
public interface ICssProvider
Task<Css> GetCss(string fileName);
public class CssProvider : ICssProvider
private readonly IFileProvider _fileProvider;
private Css _css;
public CssProvider(IFileProvider fileProvider)
_fileProvider = fileProvider;
public async Task<Css> GetCss(string fileName)
if (_css is null)
var cssFile = _fileProvider.GetFileInfo(fileName);
using (var reader = new StreamReader(cssFile.CreateReadStream()))
var cssString = await reader.ReadToEndAsync();
_css = CssParser.Default.Parse(cssString);
Debug.WriteLine($"fileName loaded.");
return _css;
This is registered in Startup
as
services.AddScoped<CssProvider>();
Example
When the tag-helper finds an element like this one:
<h2 class="m-title">@ViewData["Title"]</h2>
it turns it into this:
<h2 style="color: blueviolet;">About</h2>
where the .css
is:
m-title
color: blueviolet;
Would you say it's a good solution or do you see any room for improvement?
c# html css asp.net-core
edited Feb 24 at 22:31
asked Feb 24 at 11:44
t3chb0t
32.1k54195
32.1k54195
1
Initial observation, assumingCssParser
is static (tight coupling to static dependencies), is there any way to abstract that out and have it explicitly injected via constructor?
â Nkosi
Feb 24 at 17:14
@Nkosi you're right, it should be injected. I actually have an interface for it, it's theICssParser
and I am able to do it correctly, but I also have thisDefault
property in case I'm too lazy for a full DI ;-)
â t3chb0t
Feb 24 at 18:46
The CSS provider can cache the filename mapped to the CSS in a dictionary. The current design uses the same one for all file names after the initial set.
â Nkosi
Feb 25 at 5:02
You also need to register the interface. You are only registering the implementation.
â Nkosi
Feb 25 at 5:06
@Nkosi I think this is ok because there is only one file per action and each action is one request, right? So for each request there will be only a single fine in the cache. That's what theScoped
service registration does. It is resolved one per request. And within a single request the tag-helper is created everytime it finds theclass
attribute but still this is the same request.
â t3chb0t
Feb 25 at 8:58
 |Â
show 2 more comments
1
Initial observation, assumingCssParser
is static (tight coupling to static dependencies), is there any way to abstract that out and have it explicitly injected via constructor?
â Nkosi
Feb 24 at 17:14
@Nkosi you're right, it should be injected. I actually have an interface for it, it's theICssParser
and I am able to do it correctly, but I also have thisDefault
property in case I'm too lazy for a full DI ;-)
â t3chb0t
Feb 24 at 18:46
The CSS provider can cache the filename mapped to the CSS in a dictionary. The current design uses the same one for all file names after the initial set.
â Nkosi
Feb 25 at 5:02
You also need to register the interface. You are only registering the implementation.
â Nkosi
Feb 25 at 5:06
@Nkosi I think this is ok because there is only one file per action and each action is one request, right? So for each request there will be only a single fine in the cache. That's what theScoped
service registration does. It is resolved one per request. And within a single request the tag-helper is created everytime it finds theclass
attribute but still this is the same request.
â t3chb0t
Feb 25 at 8:58
1
1
Initial observation, assuming
CssParser
is static (tight coupling to static dependencies), is there any way to abstract that out and have it explicitly injected via constructor?â Nkosi
Feb 24 at 17:14
Initial observation, assuming
CssParser
is static (tight coupling to static dependencies), is there any way to abstract that out and have it explicitly injected via constructor?â Nkosi
Feb 24 at 17:14
@Nkosi you're right, it should be injected. I actually have an interface for it, it's the
ICssParser
and I am able to do it correctly, but I also have this Default
property in case I'm too lazy for a full DI ;-)â t3chb0t
Feb 24 at 18:46
@Nkosi you're right, it should be injected. I actually have an interface for it, it's the
ICssParser
and I am able to do it correctly, but I also have this Default
property in case I'm too lazy for a full DI ;-)â t3chb0t
Feb 24 at 18:46
The CSS provider can cache the filename mapped to the CSS in a dictionary. The current design uses the same one for all file names after the initial set.
â Nkosi
Feb 25 at 5:02
The CSS provider can cache the filename mapped to the CSS in a dictionary. The current design uses the same one for all file names after the initial set.
â Nkosi
Feb 25 at 5:02
You also need to register the interface. You are only registering the implementation.
â Nkosi
Feb 25 at 5:06
You also need to register the interface. You are only registering the implementation.
â Nkosi
Feb 25 at 5:06
@Nkosi I think this is ok because there is only one file per action and each action is one request, right? So for each request there will be only a single fine in the cache. That's what the
Scoped
service registration does. It is resolved one per request. And within a single request the tag-helper is created everytime it finds the class
attribute but still this is the same request.â t3chb0t
Feb 25 at 8:58
@Nkosi I think this is ok because there is only one file per action and each action is one request, right? So for each request there will be only a single fine in the cache. That's what the
Scoped
service registration does. It is resolved one per request. And within a single request the tag-helper is created everytime it finds the class
attribute but still this is the same request.â t3chb0t
Feb 25 at 8:58
 |Â
show 2 more comments
1 Answer
1
active
oldest
votes
up vote
2
down vote
accepted
Initial observation, assuming CssParser
was static (tight coupling to static dependencies), was to abstract that core functionality out into its own concern and have it explicitly injected via constructor.
That was done. However, the extracted helper service implementation was registered like
services.AddScoped<CssProvider>();
While it works, for a more SOLID design, classes should depend on abstractions and not concretions.
An abstraction exists, so I suggest refactoring the helper to explicitly depend on ICssProvider
abstraction
//...code removed for brevity
private readonly ICssProvider _cssProvider;
public InlineClassTagHelper(ICssProvider cssProvider)
_cssProvider = cssProvider;
//...code removed for brevity
and register the abstraction with its implementation in the composition root
(Startup) with the following extension
services.AddScoped<ICssProvider, CssProvider>();
Oh, I don't know how I missed that it takes two generic parameters! :-o
â t3chb0t
Feb 27 at 4:58
add a comment |Â
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
2
down vote
accepted
Initial observation, assuming CssParser
was static (tight coupling to static dependencies), was to abstract that core functionality out into its own concern and have it explicitly injected via constructor.
That was done. However, the extracted helper service implementation was registered like
services.AddScoped<CssProvider>();
While it works, for a more SOLID design, classes should depend on abstractions and not concretions.
An abstraction exists, so I suggest refactoring the helper to explicitly depend on ICssProvider
abstraction
//...code removed for brevity
private readonly ICssProvider _cssProvider;
public InlineClassTagHelper(ICssProvider cssProvider)
_cssProvider = cssProvider;
//...code removed for brevity
and register the abstraction with its implementation in the composition root
(Startup) with the following extension
services.AddScoped<ICssProvider, CssProvider>();
Oh, I don't know how I missed that it takes two generic parameters! :-o
â t3chb0t
Feb 27 at 4:58
add a comment |Â
up vote
2
down vote
accepted
Initial observation, assuming CssParser
was static (tight coupling to static dependencies), was to abstract that core functionality out into its own concern and have it explicitly injected via constructor.
That was done. However, the extracted helper service implementation was registered like
services.AddScoped<CssProvider>();
While it works, for a more SOLID design, classes should depend on abstractions and not concretions.
An abstraction exists, so I suggest refactoring the helper to explicitly depend on ICssProvider
abstraction
//...code removed for brevity
private readonly ICssProvider _cssProvider;
public InlineClassTagHelper(ICssProvider cssProvider)
_cssProvider = cssProvider;
//...code removed for brevity
and register the abstraction with its implementation in the composition root
(Startup) with the following extension
services.AddScoped<ICssProvider, CssProvider>();
Oh, I don't know how I missed that it takes two generic parameters! :-o
â t3chb0t
Feb 27 at 4:58
add a comment |Â
up vote
2
down vote
accepted
up vote
2
down vote
accepted
Initial observation, assuming CssParser
was static (tight coupling to static dependencies), was to abstract that core functionality out into its own concern and have it explicitly injected via constructor.
That was done. However, the extracted helper service implementation was registered like
services.AddScoped<CssProvider>();
While it works, for a more SOLID design, classes should depend on abstractions and not concretions.
An abstraction exists, so I suggest refactoring the helper to explicitly depend on ICssProvider
abstraction
//...code removed for brevity
private readonly ICssProvider _cssProvider;
public InlineClassTagHelper(ICssProvider cssProvider)
_cssProvider = cssProvider;
//...code removed for brevity
and register the abstraction with its implementation in the composition root
(Startup) with the following extension
services.AddScoped<ICssProvider, CssProvider>();
Initial observation, assuming CssParser
was static (tight coupling to static dependencies), was to abstract that core functionality out into its own concern and have it explicitly injected via constructor.
That was done. However, the extracted helper service implementation was registered like
services.AddScoped<CssProvider>();
While it works, for a more SOLID design, classes should depend on abstractions and not concretions.
An abstraction exists, so I suggest refactoring the helper to explicitly depend on ICssProvider
abstraction
//...code removed for brevity
private readonly ICssProvider _cssProvider;
public InlineClassTagHelper(ICssProvider cssProvider)
_cssProvider = cssProvider;
//...code removed for brevity
and register the abstraction with its implementation in the composition root
(Startup) with the following extension
services.AddScoped<ICssProvider, CssProvider>();
edited Feb 27 at 4:10
answered Feb 27 at 2:30
Nkosi
1,870619
1,870619
Oh, I don't know how I missed that it takes two generic parameters! :-o
â t3chb0t
Feb 27 at 4:58
add a comment |Â
Oh, I don't know how I missed that it takes two generic parameters! :-o
â t3chb0t
Feb 27 at 4:58
Oh, I don't know how I missed that it takes two generic parameters! :-o
â t3chb0t
Feb 27 at 4:58
Oh, I don't know how I missed that it takes two generic parameters! :-o
â t3chb0t
Feb 27 at 4:58
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f188262%2ftaghelper-for-class-inlining%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
1
Initial observation, assuming
CssParser
is static (tight coupling to static dependencies), is there any way to abstract that out and have it explicitly injected via constructor?â Nkosi
Feb 24 at 17:14
@Nkosi you're right, it should be injected. I actually have an interface for it, it's the
ICssParser
and I am able to do it correctly, but I also have thisDefault
property in case I'm too lazy for a full DI ;-)â t3chb0t
Feb 24 at 18:46
The CSS provider can cache the filename mapped to the CSS in a dictionary. The current design uses the same one for all file names after the initial set.
â Nkosi
Feb 25 at 5:02
You also need to register the interface. You are only registering the implementation.
â Nkosi
Feb 25 at 5:06
@Nkosi I think this is ok because there is only one file per action and each action is one request, right? So for each request there will be only a single fine in the cache. That's what the
Scoped
service registration does. It is resolved one per request. And within a single request the tag-helper is created everytime it finds theclass
attribute but still this is the same request.â t3chb0t
Feb 25 at 8:58