-
Notifications
You must be signed in to change notification settings - Fork 10k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Discussion: bringing back @helper for views #5110
Comments
Just found this porting endeavor in our codebase. The dynamic workaround you mention doesn't need to be dynamic. It can be strongly typed: Before: @helper SomeHelper(SomeType item) {
/* CODE */
} After: @{Func<SomeType, IHtmlContent> SomeHelper = @<text>@{
/* CODE */
}</text>;} |
Thanks for contacting us, @NickCraver. |
The workaround is a syntactic mess. A good short-term workaround, but hardly a replacement. |
I need to setup benchmarks here (won't happen before the break), but we'd be allocating the function on every razor render, aren't we? That'd be a near-linear scale of cost with the size of the helper. Going to try and play with more options here to see what's allowed. Note: I think the |
If you have a good representative benchmark it would be great to add something to https://github.com/aspnet/Mvc/tree/master/benchmarkapps/RazorRendering - @benaadams contributed what's there, and it's immensely useful to us to get real examples. |
@rynowak Thanks for the pointer! We dug into this today and were a little surprised there were no render time benchmarks directly runnable in the Razor repo - it looks like everything these is compiler performance. In the Mvc repo you can do some benchmarking with external tool captures, but we're thinking there should be some BenchmarkDotNet benchmarks readily runnable here. I get that Razor -> If for some reason we can't do that, we'll just razor gen the various options and throw the |
FYI in case you haven't seen the announcements - we're in progress of moving repos around. MVC and Razor's runtime support libraries will be moving to aspnet/AspNetCore and the Razor compiler features will be moving somewhere else. |
I've gotten the Benchmark app to compile & execute a razor page: but this doesn't help much, since all of the things that actually run are implemented in https://github.com/aspnet/Mvc. Would it be OK to pull in MVC package refs for benchmarks? |
You can make it less of a mess as well as support multiple parameters by using an extension method. |
Looks like benchmarking render times only makes sense with MVC in play, too. I've forked my benchmarks off there: |
My preferred solution for a new feature to supplant One of the things that's clunky is that you have to put it inside a code block since it's void-returning. We don't have a way to know at codegen time if a method is void or not. In views, this is a really simple solution because all of the state that Razor needs is part of the instance. This also has the benefit that it doesn't need to allocate an In components, I can't think of a reasonable solution other than allocating a In a view
Related to this proposal, the only major pivot here I see is related to async. For components, the answer is clear, everything in the render path is sync. For views it seems at first that we have a few options:
Doing either 1 or 2 requires us to parse method signatures (ugh) and are more limited so doing 3 would be less work. The perils that you run into as a user if we make it flexible are the same as one might experience with normal C# async methods, but viewed through a fog. a. If you define an async method but don't await the calls, you have a bug Let's think about how we can mitigate all of these. a. We could address this with an analzyer. I'm not sure what our plans are like in the shared framework world for delivering analyzers 😞 A slightly bigger hammer would be to overload b. The nice thing about this is that using a tag helper doesn't add the c. I don't really have any idea for how to mitigate this. My usability concern with this is that tag helpers like Here's some perf results: BenchmarkDotNet=v0.10.13, OS=Windows 10.0.17763
Intel Xeon CPU E5620 2.40GHz, 1 CPU, 4 logical cores and 4 physical cores
.NET Core SDK=3.0.100-preview-009750
[Host] : .NET Core 3.0.0-preview1-26907-05 (CoreCLR 4.6.26907.04, CoreFX 4.6.26907.04), 64bit RyuJIT
Job-HUYTCC : .NET Core 3.0.0-preview1-26907-05 (CoreCLR 4.6.26907.04, CoreFX 4.6.26907.04), 64bit RyuJIT
Runtime=Core Server=True Toolchain=.NET Core 3.0
RunStrategy=Throughput
#InliningWin |
Noting here: we didn't get pinged on this milestone change (I guess GitHub doesn't do that). Stack Overflow's ASP.NET Core migration is still blocked by this. The delay to preview 3 is very frustrating as it hoses a lot of downstream plans indefinitely. When will this land? |
Hi @NickCraver, Unfortunately this didn't land for Preview 2. We've been redoing our build infrastructure and unfortunately that took more time than expected. Since this feature will require VS work to enable the corresponding Razor tooling, we need to align this with work with a corresponding VS release. It's too late at this point to add this to the initial release of VS2019, so our new plan is to get this into the first preview of the first update to VS2019, which should align with .NET Core 3.0 Preview 4. I've update the target milestone accordingly. Sorry about the inconvenience! Let us know if you have any further questions or concerns. |
We also used this allot in exactly the same way |
- Allow markup to exist in all code blocks. Prior to this change whenever we'd see nested curly braces we would do dumb brace matching to skip over any potentially risky code; now we treat every curly brace as an opportunity to intermingle Markup. - One fix I had to introduce was now that functions blocks are parsed like `@{}` blocks I ran into cases where certain reserved keywords would get improperly parsed. This exposed a bug in our parsing where we’d treat **class** and **namespace** as directives without a transition in a `@{}` block. For instance this: ``` @{ class } ``` would barf in the old parser by treating the `class` piece as a directive even though it did not have a transition. To account for this I changed our reserved directives to be parsed as directives instead of keywords (it's how they should have been parsed anyhow). This isn't a breaking change because the directive parsing logic is a subset of how keywords get parsed. - One quirk this change introduces is a difference in behavior in regards to one error case. Before this change if you were to have `@if (foo)) { var bar = foo; }` the entire statement would be classified as C# and you'd get a C# error on the trailing `)`. With my changes we try to keep group statements together more closely and allow for HTML in unexpected or end of statement scenarios. So, with these new changes the above example would only have `@if (foo))` classified as C# and the rest as markup because the original was invalid. - Added lots of tests, - Modified the feature flag to maintain the old behavior when disabled. dotnet/aspnetcore#5110
- Allow markup to exist in all code blocks. Prior to this change whenever we'd see nested curly braces we would do dumb brace matching to skip over any potentially risky code; now we treat every curly brace as an opportunity to intermingle Markup. - One fix I had to introduce was now that functions blocks are parsed like `@{}` blocks I ran into cases where certain reserved keywords would get improperly parsed. This exposed a bug in our parsing where we’d treat **class** and **namespace** as directives without a transition in a `@{}` block. For instance this: ``` @{ class } ``` would barf in the old parser by treating the `class` piece as a directive even though it did not have a transition. To account for this I changed our reserved directives to be parsed as directives instead of keywords (it's how they should have been parsed anyhow). This isn't a breaking change because the directive parsing logic is a subset of how keywords get parsed. - One quirk this change introduces is a difference in behavior in regards to one error case. Before this change if you were to have `@if (foo)) { var bar = foo; }` the entire statement would be classified as C# and you'd get a C# error on the trailing `)`. With my changes we try to keep group statements together more closely and allow for HTML in unexpected or end of statement scenarios. So, with these new changes the above example would only have `@if (foo))` classified as C# and the rest as markup because the original was invalid. - Added lots of tests, - Modified the feature flag to maintain the old behavior when disabled. dotnet/aspnetcore#5110
In porting some existing applications (from ASP.NET MVC 5), we're seeing immense amounts of pain with views that use
@helper
today.@helper
, from the user view point, is a very simple way to use Razor templating to re-use some code. Perhaps to render a date a certain way, or a chart, or a graph, or whatever. As an example, Stack Overflow's codebase has 458@helper
s today.Some history:
@helper
was removed in #281 after findings in Add support for using @helper across views aspnet/Mvc#1130But
@helper
still lacks an equally (or even close) usable replacement. The alternatives I'm aware of today are:dynamic
workaround which isn't type safe and I'm not sure on the performance implications ofThere are 2 ways
@helper
worked in ASP.NET MVC 5: globally from App_Data and in individual views. I am only advocating for support of the latter. IMO, Tag Helpers or view components (pick your flavor!) are excellent replacements for the global use case. That makes total sense and having another view file is also reasonable.Let's say we have a view today with 5
@helper
sections in ASP.NET MVC 5. In ASP.NET Core, it's a regression. We have a few options:dynamic
approach, eat the performance, and hope it doesn't go boom at runtimeNote: I am not sure about the performance differences on these. That needs to be tested. I'll try and get benchmarks going when time allows. I have a hard time imagining any of these would be as fast as what
@helper
used to do, though. But I take nothing for a given here...we need to benchmark, even if@helper
isn't a candidate yet.A lot of users have chimed in on closed issues above. A lot of migration pain is being felt. Probably much more than was envisioned when
@helper
was removed. This is one of the largest issues I see in our migrations and it's something I'm really feeling right now. Is there a possibility we bring@helper
or equivalent functionality (re-using some Razor templating inside the same view) back?cc @DamianEdwards @rynowak
The text was updated successfully, but these errors were encountered: