-
Notifications
You must be signed in to change notification settings - Fork 138
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
Possible fix for .NET 8 Blazor Web App breakage #481
base: master
Are you sure you want to change the base?
Conversation
…ync due to change in lifecycle, but only for .NET 8
There's unfortunately more that needs tackling here. I built a local version of Fluxor that only target .NET 8 to simplify this and it builds fine now, but while the Counter page loads without error when
|
Fancy pairing on this one if the days? I'm available from 5PM week days (UK time) |
Sure - we can sync on that next week. I'm in the US, but if I did the math right, that puts me at 11 AM or later, so that's more than fine. I've identified at least part of the problem. In .NET 7, you have either a server or a WASM project. You register Fluxor via the dependency injection scheme and initialize the store by placing the In .NET 8, you cannot quite do this because as the library author, you're not going to know what render mode the user is opting for as it can be done on a per-page/component basis instead of globally, so you've got to be a bit more flexible about how the service is initialized. On the Server project, the However, on the client, we've got several limitations to consider:
As such, on the Client project, in addition to the local Fluxor DI registration mentioned above, I propose that because /// <summary>
/// Method invoked when the component has received parameters from its parent in
/// the render tree, and the incoming values have been assigned to properties.
/// </summary>
/// <returns>A <see cref="T:System.Threading.Tasks.Task" /> representing any asynchronous operation.</returns>
protected override async Task OnParametersSetAsync()
{
await base.OnParametersSetAsync();
//Attempt to initialize the store knowing that if it's already been initialized, this won't do anything.
await Store.InitializeAsync();
} As the Store has a flag checking if it's already been initialized, if this runs and that flag is true, nothing will happen, but because all Fluxor components are expected to inherit a There's no need to change the FluxorOptions to prefer a Singleton over a Scoped registration (as the Client is going to treat is as a Singleton anyway). There's also no need to disable prerender to make this work. I've tested that it works on interactive auto whether prerender is enabled or not. I've tested this several times and it now works precisely as expected on both the Server and, after waiting for it to load the WASM bundle, switching pages and switching back to validate it's disconnected from the server, successfully verified it works on the Client as well. I'll update the PR shortly with my proposed changes. Remaining open work:
|
I've added a single persistence layer to Fluxor that appears to resolve the cross-rendermode issue. As observed by others on another thread about this, it's one thing to get Fluxor up and running again, but a whole other thing to completely lose your state as soon as you jump render modes (e.g. server -> web assembly). While I've taken a stab at implementing this in a lightweight manner, it wasn't until I was integrating the changes from my test project to this branch that I realized my solution is only compatible with .NET 6 and higher because of my use of A .NET 8 version of this can be seen on the latest commit here, but I'll take a moment to walk through the idea. I built off the idea of using an action to identify when the state has initialized and an effect that triggers from that. I've added some extensions to the registration method that allows the developer to opt into the persistence feature and provide an implementation of If it is registered, when Fluxor is activating the store instance, it first marks the store as activated, dequeues any actions and then dispatches a new On a location change event in the Fluxor component, it dispatches a This implementation avoids having the project take on any other package dependencies and leaves it to the developer to decide where and how they want to persist any data, whether that be in session state as my demo does or bind it to a session ID and save it somewhere on the server - their choice. Just implement the Remaining work:
|
@WhitWaldo thanks for the effort in getting this working ❤️
|
It'll be in 6.0 I will be releasing Beta versions, but not until they are stable so should be okay to use. |
Great stuff, thanks @mrpmorris 🙂 |
I've made a few changes to the PR and also updated my .NET 8 demonstration to reflect them. As before, I can't actually build this locally as I'm missing some of these older .NET SDKs, but I copy/pasted the changed pieces from my demo, so I'm fairly confident it should work.
At this point, this is working well enough for my purposes that I don't know any other active work is really necessary unless you really want to replace the I don't see a branch that specifically identifies as the 6.0 release, but if you can point me in the right direction, I'd be happy to apply my changes to that branch as well in a separate PR. |
@@ -15,9 +15,11 @@ | |||
</ItemGroup> | |||
|
|||
<ItemGroup> | |||
<PackageReference Condition="'$(TargetFramework)' == 'net8.0'" Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" /> | |||
<PackageReference Condition="'$(TargetFramework)' == 'net7.0'" Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" /> | |||
<PackageReference Condition="'$(TargetFramework)' == 'net6.0'" Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" /> | |||
<PackageReference Condition="'$(TargetFramework)' == 'net5.0'" Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="5.0.0" /> | |||
<PackageReference Condition="'$(TargetFramework)' != 'net7.0' AND '$(TargetFramework)' != 'net6.0' AND '$(TargetFramework)' != 'net5.0'" Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.0" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.0
does this need to be removed or a condition added for != 'net8.0
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I rather assumed that whatever was there was compatible up to the current supported build prior to my adding .NET 8 to the list - presumably that includes .NET Core 3.1 as-is if that's still a supported target, so I wouldn't think anything else is necessary.
@@ -14,6 +21,10 @@ public class Store : IStore, IActionSubscriber, IDisposable | |||
/// <see cref="IStore.Initialized"/> | |||
public Task Initialized => InitializedCompletionSource.Task; | |||
|
|||
#if NET6_0_OR_GREATER | |||
private readonly IPersistenceManager? _persistenceManager; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider following naming convention of project.? Uppercase vs leading underscore for privates.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I defer to the maintainer's preference here.
Will this PR be in beta#3/V6? We make heavy use of Fluxor and are moving to a .NET8 hybrid Blazor server/client render model and are facing the issue that actions are no longer being dispatched. |
Any news on this PR being available for a beta release ? We're in the same situation as @mikeppcom |
try specifying the @rendermode in |
Adds .NET 8 as a valid target in addition to .NET 7 and the other versions.
Also moves state initialization out from
OnAfterRenderAsync
(which doesn't run on server anymore per the docs) toOnInitializedAsync
which runs after existing logic inOnInitialized
and afterOnParametersSet
(which in .NET 8 now runs beforeOnInitialized{Async}
), so it's the last lifecycle method beforeOnAfterRender
, assuming it were still being called.I'm having problems getting Fluxor.Blazor.Web to build on my machine (likely missing one of your targets) so I haven't yet tested this, but as covered in the related issue,, when I set a breakpoint in the ActionDispatched method, I can see that the actions are accumulating in the Store and they don't dequeue because the store never sets
HasActivatedStore
to true, which only happens via this call toStore.InitializeAsync()
which only happens inOnAfterRenderAsync
which isn't called on the server anymore, so it intuitively feels like this might fix the problem.I'll follow up if I'm able to validate it locally furhter.