Skip to content
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

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions Source/Lib/Fluxor.Blazor.Web/Components/FluxorComponent.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using Fluxor.UnsupportedClasses;
using Microsoft.AspNetCore.Components;
using System.Threading;
using System;
using System.Threading.Tasks;

namespace Fluxor.Blazor.Web.Components
{
Expand All @@ -13,14 +15,17 @@ public abstract class FluxorComponent : ComponentBase, IDisposable
[Inject]
private IActionSubscriber ActionSubscriber { get; set; }

[Inject]
private IStore Store { get; set; }

private bool Disposed;
private IDisposable StateSubscription;
private readonly ThrottledInvoker StateHasChangedThrottler;

/// <summary>
/// Creates a new instance
/// </summary>
public FluxorComponent()
protected FluxorComponent()
{
StateHasChangedThrottler = new ThrottledInvoker(() =>
{
Expand Down Expand Up @@ -63,17 +68,25 @@ public void Dispose()
Disposed = true;
}
}

/// <summary>
/// Subscribes to state properties
/// </summary>
protected override void OnInitialized()
{
base.OnInitialized();
StateSubscription = StateSubscriber.Subscribe(this, _ =>
{
StateHasChangedThrottler.Invoke(MaximumStateChangedNotificationsPerSecond);
});

base.OnInitialized();
}

protected override async Task OnInitializedAsync()
{
//Attempt to initialize the store knowing that if it's already been initialized, this won't do anything.
await Store.InitializeAsync();
await base.OnInitializedAsync();
}

protected virtual void Dispose(bool disposing)
Expand Down
2 changes: 2 additions & 0 deletions Source/Lib/Fluxor.Blazor.Web/Fluxor.Blazor.Web.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.0" Condition="'$(TargetFramework)' == 'net8.0'" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="7.0.0" Condition="'$(TargetFramework)' == 'net7.0'" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="6.0.0" Condition="'$(TargetFramework)' == 'net6.0'" />

<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" Condition="'$(TargetFramework)' == 'net8.0'" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" Condition="'$(TargetFramework)' == 'net7.0'" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" Condition="'$(TargetFramework)' == 'net6.0'" />
</ItemGroup>
Expand Down
59 changes: 59 additions & 0 deletions Source/Lib/Fluxor.Blazor.Web/Persistence/PersistenceEffects.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using Fluxor.Persistence;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace Fluxor.Blazor.Web.Persistence
{
#if NET6_0_OR_GREATER
public sealed class PersistenceEffects
{
private readonly IPersistenceManager _persistenceManager;
private readonly IServiceProvider _serviceProvider;

public PersistenceEffects(IPersistenceManager persistenceManager, IServiceProvider serviceProvider)
{
_persistenceManager = persistenceManager;
_serviceProvider = serviceProvider;
}

/// <summary>
/// Maintains a reference to IStore - injected this way to avoid a circular dependency during the effect method registration
/// </summary>
private readonly Lazy<IStore> _store = new(_serviceProvider.GetRequiredService<IStore>);

[EffectMethod(typeof(StorePersistingAction))]
public async Task PersistStoreData(IDispatcher dispatcher)
{
//Serialize the store
var json = _store.Value.SerializeToJson();

//Save to the persistence manager
await _persistenceManager.PersistStoreToStateAsync(json);

//Completed
dispatcher.Dispatch(new StorePersistedAction());
}

[EffectMethod(typeof(StoreRehydratingAction))]
public async Task RehydrateStoreData(IDispatcher dispatcher)
{
//Read from the persistence manager
var serializedStore = await _persistenceManager.RehydrateStoreFromStateAsync();
if (serializedStore is null)
{
//Nothing to rehydrate - leave as-is
dispatcher.Dispatch(new StoreRehydratedAction());
return;
}

_store.Value.RehydrateFromJson(serializedStore);

//Completed
dispatcher.Dispatch(new StoreRehydratedAction());
}
}
#endif
}
10 changes: 10 additions & 0 deletions Source/Lib/Fluxor.Blazor.Web/StoreInitializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ protected override void OnInitialized()
base.OnInitialized();
}

#if NET8
protected override async Task OnInitializedAsync()
{
await Store.InitializeAsync();
await base.OnInitializedAsync();
}
#endif

protected override void OnAfterRender(bool firstRender)
{
base.OnAfterRender(firstRender);
Expand All @@ -73,7 +81,9 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
if (!string.IsNullOrWhiteSpace(MiddlewareInitializationScripts))
await JSRuntime.InvokeVoidAsync("eval", MiddlewareInitializationScripts);

#if !NET8
await Store.InitializeAsync();
#endif
}
catch (JSException err)
{
Expand Down
28 changes: 28 additions & 0 deletions Source/Lib/Fluxor/DependencyInjection/FluxorOptions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Fluxor.Extensions;
using Fluxor.Persistence;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -138,5 +139,32 @@ public FluxorOptions AddMiddleware<TMiddleware>()
.ToArray();
return this;
}

#if NET6_0_OR_GREATER

/// <summary>
/// Enables automatic store persistence between location state changes.
/// </summary>
/// <typeparam name="T">The type of persistence implementation to use.</typeparam>
/// <returns>Options</returns>
public FluxorOptions WithPersistence<T>() where T : IPersistenceManager
{
Services.AddScoped(typeof(IPersistenceManager), typeof(T));
return this;
}

/// <summary>
/// Enables automatic store persistence between location state changes.
/// </summary>
/// <typeparam name="T">The type of persistence implementation to use.</typeparam>
/// <returns>Options</returns>
public FluxorOptions WithPersistence<T>(IServiceCollection serviceCollection) where T : IPersistenceManager
{
WithPersistence<T>();
foreach (var serviceDescriptor in serviceCollection) Services.Add(serviceDescriptor);
return this;
}

#endif
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Fluxor.DependencyInjection.WrapperFactories;
using Fluxor.Extensions;
using Fluxor.Persistence;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -41,7 +42,13 @@ public static void Register(
services.Add(typeof(Store), serviceProvider =>
{
var dispatcher = serviceProvider.GetService<IDispatcher>();

#if NET6_0_OR_GREATER
var persistenceManager = serviceProvider.GetService<IPersistenceManager>();
var store = new Store(dispatcher, persistenceManager);
#else
var store = new Store(dispatcher);
#endif
foreach (FeatureClassInfo featureClassInfo in featureClassInfos)
{
var feature = (IFeature)serviceProvider.GetService(featureClassInfo.FeatureInterfaceGenericType);
Expand Down
4 changes: 3 additions & 1 deletion Source/Lib/Fluxor/Fluxor.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<Description>A zero boilerplate Redux/Flux framework for .NET</Description>
<PackageIcon>fluxor-small-logo.png</PackageIcon>
<PackageTags>Redux Flux DotNet CSharp</PackageTags>
<SignAssembly Condition="'$(Configuration)'=='Release'" >True</SignAssembly>
<SignAssembly Condition="'$(Configuration)'=='Release'">True</SignAssembly>
</PropertyGroup>

<ItemGroup>
Expand All @@ -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" />
Copy link
Contributor

@jafin jafin Apr 13, 2024

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 ?

Copy link
Author

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.

<PackageReference Include="System.Text.Json" Version="4.7.2" />
</ItemGroup>
</Project>
13 changes: 13 additions & 0 deletions Source/Lib/Fluxor/IStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,5 +93,18 @@ public interface IStore : IActionSubscriber
/// Executed when an exception is not handled
/// </summary>
event EventHandler<Exceptions.UnhandledExceptionEventArgs> UnhandledException;

/// <summary>
/// Persists the features in the store to a serialized string.
/// </summary>
/// <returns></returns>
string SerializeToJson();

/// <summary>
/// Deserializes a previously serialized store and rehydrates each feature using the
/// provided values.
/// </summary>
/// <param name="json">The serialized store.</param>
void RehydrateFromJson(string json);
}
}
27 changes: 27 additions & 0 deletions Source/Lib/Fluxor/Persistence/IPersistenceManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace Fluxor.Persistence
{
public interface IPersistenceManager
{
/// <summary>
/// Persists the store to a persisted state.
/// </summary>
/// <param name="serializedStore">The serialized store data being persisted.</param>
public Task PersistStoreToStateAsync(string serializedStore);

/// <summary>
/// Rehydrates the store from a persisted state.
/// </summary>
public Task<string?> RehydrateStoreFromStateAsync();

/// <summary>
/// Clears the store from the persisted state.
/// </summary>
public Task ClearStoreFromStateAsync();
}
}
56 changes: 56 additions & 0 deletions Source/Lib/Fluxor/Persistence/PersistenceEffects.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace Fluxor.Persistence
{
public sealed class PersistenceEffects
{
private readonly IPersistenceManager _persistenceManager;
private readonly IServiceProvider _serviceProvider;

public PersistenceEffects(IPersistenceManager persistenceManager, IServiceProvider serviceProvider)
{
_persistenceManager = persistenceManager;
_serviceProvider = serviceProvider;
}

/// <summary>
/// Maintains a reference to IStore - injected this way to avoid a circular dependency during the effect method registration
/// </summary>
private readonly Lazy<IStore> _store = new(_serviceProvider.GetRequiredService<IStore>);

[EffectMethod(typeof(StorePersistingAction))]
public async Task PersistStoreData(IDispatcher dispatcher)
{
//Serialize the store
var json = _store.Value.SerializeToJson();

//Save to the persistence manager
await _persistenceManager.PersistStoreToStateAsync(json);

//Completed
dispatcher.Dispatch(new StorePersistedAction());
}

[EffectMethod(typeof(StoreRehydratingAction))]
public async Task RehydrateStoreData(IDispatcher dispatcher)
{
//Read from the persistence manager
var serializedStore = await _persistenceManager.RehydrateStoreFromStateAsync();
if (serializedStore is null)
{
//Nothing to rehydrate - leave as-is
dispatcher.Dispatch(new StoreRehydratedAction());
return;
}

_store.Value.RehydrateFromJson(serializedStore);

//Completed
dispatcher.Dispatch(new StoreRehydratedAction());
}
}
}
13 changes: 13 additions & 0 deletions Source/Lib/Fluxor/Persistence/StorePersistedAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Fluxor.Persistence
{
/// <summary>
/// Dispatched by the store once it has been successfully persisted to state.
/// </summary>
public sealed class StorePersistedAction
{
}
}
13 changes: 13 additions & 0 deletions Source/Lib/Fluxor/Persistence/StorePersistingAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Fluxor.Persistence
{
/// <summary>
/// Dispatched by the store once it has started persisting to state.
/// </summary>
public sealed class StorePersistingAction
{
}
}
13 changes: 13 additions & 0 deletions Source/Lib/Fluxor/Persistence/StoreRehydratedAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Fluxor.Persistence
{
/// <summary>
/// Dispatched by the store once it has been successfully rehydrated from state.
/// </summary>
public sealed class StoreRehydratedAction
{
}
}
13 changes: 13 additions & 0 deletions Source/Lib/Fluxor/Persistence/StoreRehydratingAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Fluxor.Persistence
{
/// <summary>
/// Dispatched by the store once it has started rehydrating from state.
/// </summary>
public sealed class StoreRehydratingAction
{
}
}
Loading