Skip to content

Environments + client services w/prerendering#31057

Merged
guardrex merged 15 commits into
mainfrom
guardrex/blazor-environments
Nov 17, 2023
Merged

Environments + client services w/prerendering#31057
guardrex merged 15 commits into
mainfrom
guardrex/blazor-environments

Conversation

@guardrex
Copy link
Copy Markdown
Collaborator

@guardrex guardrex commented Nov 16, 2023

Fixes #31025
Addresses #28161

Mackinnon ... I decided to draft this with a full explanation and examples for what's going on with client-side services in a BWA. It's a little long. However, I can understand it this way, so we have a good bet on not losing anyone.

In the Render Modes article, the Set the render mode for the entire app section was too far down the article. I moved the section up and changed the heading name to Apply a render mode to the entire app. The new section on Client-side services fail to resolve during prerendering makes reference to globally setting the render mode. I want to reader to have already seen that section.


Internal previews

📄 File 🔗 Preview link
aspnetcore/blazor/components/index.md ASP.NET Core Razor components
aspnetcore/blazor/components/render-modes.md ASP.NET Core Blazor render modes
aspnetcore/blazor/fundamentals/configuration.md ASP.NET Core Blazor configuration
aspnetcore/blazor/fundamentals/environments.md aspnetcore/blazor/fundamentals/environments
aspnetcore/blazor/security/server/index.md Secure ASP.NET Core server-side Blazor apps
aspnetcore/blazor/state-management.md ASP.NET Core Blazor state management

@guardrex guardrex self-assigned this Nov 16, 2023
@guardrex guardrex changed the title Guardrex/blazor environments Environments + client services w/prerendering Nov 16, 2023
Copy link
Copy Markdown
Member

@MackinnonBuck MackinnonBuck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a great start - I have some ideas and thoughts!

Comment thread aspnetcore/blazor/components/render-modes.md Outdated
Comment thread aspnetcore/blazor/components/render-modes.md Outdated
Comment thread aspnetcore/blazor/components/render-modes.md Outdated
Comment thread aspnetcore/blazor/components/render-modes.md Outdated
Comment thread aspnetcore/blazor/components/render-modes.md Outdated
Comment thread aspnetcore/blazor/components/render-modes.md Outdated
Comment thread aspnetcore/blazor/fundamentals/environments.md Outdated
Comment thread aspnetcore/blazor/fundamentals/environments.md Outdated
Comment thread aspnetcore/blazor/fundamentals/environments.md Outdated
Comment thread aspnetcore/blazor/fundamentals/environments.md Outdated
Comment thread aspnetcore/blazor/components/render-modes.md Outdated
guardrex and others added 2 commits November 16, 2023 15:20
Co-authored-by: Mackinnon Buck <mackinnon.buck@gmail.com>
@guardrex
Copy link
Copy Markdown
Collaborator Author

guardrex commented Nov 16, 2023

@MackinnonBuck ... I think I have most of the 🦖 RexHacks!™ 🦖 out of the PR. 🙈

@guardrex
Copy link
Copy Markdown
Collaborator Author

guardrex commented Nov 16, 2023

... and I changed the section name in the Environments topic ...

- Read the client-side environment in a Blazor Web App
+ Read the environment in a Blazor Web App

... because the environment is being read in both and this has broader implications because it also applies to Auto components.

UPDATE: I added a couple of more commits 👇, and I think I'm good now.

Copy link
Copy Markdown
Member

@MackinnonBuck MackinnonBuck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good! I do also want to hear an opinion from @javiercn or @SteveSandersonMS before we officially suggest this approach to customers.

Comment thread aspnetcore/blazor/fundamentals/environments.md Outdated
Comment thread aspnetcore/blazor/fundamentals/environments.md Outdated
Comment thread aspnetcore/blazor/fundamentals/environments.md Outdated
@guardrex
Copy link
Copy Markdown
Collaborator Author

@MackinnonBuck ... Looks good, but can we use IAppEnvironment? Trying to keep it short to keep the code line lengths at 85 or less chars.

@MackinnonBuck
Copy link
Copy Markdown
Member

@MackinnonBuck ... Looks good, but can we use IAppEnvironment? Trying to keep it short to keep the code line lengths at 85 or less chars.

That sounds fine to me! 🙂


This error occurs because the component must compile and execute on the server during prerendering, but <xref:Microsoft.AspNetCore.Components.WebAssembly.Hosting.IWebAssemblyHostEnvironment> isn't a registered service on the server.

There are a four approaches that you can take to address this scenario. The following are listed from most recommended to least recommended:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Often a simpler option is to inject IServiceProvider instead of the service type itself, and then you can have code like:

if (Services.GetService<IWebAssemblyHostEnvironment>() is {} env)
{
   // ... do stuff with env
}

TBH that seems easier than making a new abstraction.

Copy link
Copy Markdown
Collaborator Author

@guardrex guardrex Nov 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AH! I see ... yes ... BUT, it won't resolve an environment at all during prerendering. If the dev is counting on the value there, this will 💥 break their logic, correct?

It looks like this would be good for only Interactive WebAssembly rendering without a prerendering value required when it executes.

UPDATE: This was @MackinnonBuck's second bullet. I've placed this guidance on the PR above the list as a solution for when the value isn't required during prerendering.

Comment thread aspnetcore/blazor/fundamentals/environments.md Outdated
@SteveSandersonMS
Copy link
Copy Markdown
Member

@guardrex @MackinnonBuck Could you clarify which specific part of this doc is the "approach" that you consider at risk of being controversial, and are asking for feedback about? I've read through it and nothing instantly springs out as being problematic, but it is a long doc and maybe I've missed something.

@SteveSandersonMS
Copy link
Copy Markdown
Member

SteveSandersonMS commented Nov 17, 2023

Oh wait, I think I see now. You're referring to the stuff about IAppEnvironment.

While this approach would certainly work, it has the drawback that it doesn't create compatibility with existing code that already depends on accessing IWebAssemblyHostEnvironment. Two other possibilities:

Implement IWebAssemblyHostEnvironment on the server

You may have considered this, not sure, but why not simply create your own service implementation for IWebAssemblyHostEnvironment on the server?

public class ServerHostEnvironment(IWebHostEnvironment env, NavigationManager nav) : IWebAssemblyHostEnvironment
{
    public string Environment => env.EnvironmentName;
    public string BaseAddress => nav.BaseUri;
}

... with builder.Services.TryRegisterScoped<IWebAssemblyHostEnvironment, ServerHostEnvironment>(). Now all existing code will just work.

Fix it in the framework

Longer term, if this is a common pain point, we could change the framework code that registers the WebAssembly hosting endpoints etc so that it also registers an implementation of IWebAssemblyHostEnvironment just like the one above, so then the whole problem vanishes for everyone. If this seems right (@MackinnonBuck?) we should track an issue for it.


## Apply a render mode to the entire app

To set the render mode for the entire app, indicate the render mode at the highest-level interactive component in the app's component hierarchy that isn't a root component.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A concrete example here will be good. It's impossible for anyone without context to understand what is being said here. (To give you an idea, I have context and I had to stop to think about it).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The definition of a render mode and how to set it for component instances and component definitions were just explained in the preceding sections. What's not clear?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not go through the previous section. Let me read through it

@guardrex
Copy link
Copy Markdown
Collaborator Author

guardrex commented Nov 17, 2023

create your own service implementation for IWebAssemblyHostEnvironment on the server?

I like it, but I recommend keeping the service-based approach listed in case it has utility in other cases.

One thing tho ... is TryRegisterScoped valid API? Did you mean TryAddScoped?

Comment on lines +149 to +150
> [!NOTE]
> Making a root component interactive, such as the `App` component, isn't supported because the Blazor script may be evaluated multiple times. Therefore, the render mode for the entire app can't be set directly by the `App` component.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the Blazor script evaluating multiple times was the issue here. I don't remember the exact details

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears in a few other spots. I'll fix it everywhere on this PR later.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my recollection, it was that the script was being evaluated multiple times.

Nonetheless we don't have to explain why it's unsupported for the root component to be interactive. We only need to communicate that it is unsupported.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fixed it by removing the reason.

Comment thread aspnetcore/blazor/components/render-modes.md Outdated
@guardrex
Copy link
Copy Markdown
Collaborator Author

@SteveSandersonMS ... I placed your approach as the top pick. I just moved the concept of custom services down to the second spot in the list, and it won't have an example in the docs. In the Environments article, I placed the full example that you provided.

@SteveSandersonMS
Copy link
Copy Markdown
Member

One thing tho ... is TryRegisterScoped valid API? Did you mean TryAddScoped?

Yeah I probably did mean TryAddScoped. Sorry. I was just typing from memory and haven't verified that the code is correct and would compile.

@guardrex
Copy link
Copy Markdown
Collaborator Author

guardrex commented Nov 17, 2023

I added the IServiceProvider approach as an approach to address the problem, but I did so above the list of complete solutions to the problem ... i.e., it isn't useful for obtaining a value during prerendering (if that's what the dev's spec calls for).

@MackinnonBuck, your bullet number 2 was @SteveSandersonMS's idea in code form. I removed that bullet from the list given that I'm moving it up above the list.

I also moved your last bullet on disabling prerendering to just under the IServiceProvider approach because that also doesn't solve the problem where the dev wants prerendering and a value.

The list is meant to include only the approaches that fully solve the problem, which is to obtain the value during prerendering. I think the list has the three good approaches.

I recommend re-reviewing the entire section along with the new example in the Environments doc.

Comment thread aspnetcore/blazor/fundamentals/environments.md Outdated
Co-authored-by: Javier Calvarro Nelson <jacalvar@microsoft.com>
@guardrex
Copy link
Copy Markdown
Collaborator Author

Going ahead with this. If there's additional feedback, I can create a new PR.

@guardrex guardrex merged commit eb2ba77 into main Nov 17, 2023
@guardrex guardrex deleted the guardrex/blazor-environments branch November 17, 2023 23:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Environments and client services with prerendering 8.0

4 participants