From c722b3890c64d391c1e212fe47a5735be63ae770 Mon Sep 17 00:00:00 2001 From: Rockford lhotka Date: Fri, 27 Sep 2024 14:47:31 -0500 Subject: [PATCH 01/10] If !SyncContextWithServer properly load empty session --- Source/Csla.Blazor.WebAssembly/State/SessionManager.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/Csla.Blazor.WebAssembly/State/SessionManager.cs b/Source/Csla.Blazor.WebAssembly/State/SessionManager.cs index b6c6c6bf95..6b5873d58f 100644 --- a/Source/Csla.Blazor.WebAssembly/State/SessionManager.cs +++ b/Source/Csla.Blazor.WebAssembly/State/SessionManager.cs @@ -36,6 +36,10 @@ public class SessionManager( /// public Session GetCachedSession() { + if (!_options.SyncContextWithServer && _session == null) + { + _session = GetSession(); + } return _session; } From 5748bafb191dc229af5e616abf6d6515c4260f40 Mon Sep 17 00:00:00 2001 From: Rockford Lhotka Date: Tue, 1 Oct 2024 15:39:40 -0500 Subject: [PATCH 02/10] #2551 Updating ProjectTracker code --- .../ProjectTracker/Directory.Packages.props | 4 +++- .../ActiveCircuitState.cs | 7 ------ .../Pages/EditProject.razor | 16 ++++++-------- .../ProjectTracker.Blazor.Client/Program.cs | 13 +++++------ .../ProjectTracker.Blazor.Client.csproj | 1 + .../RenderModeProvider.cs | 22 ------------------- .../RenderModes.cs | 10 --------- .../ActiveCircuitHandler.cs | 19 ---------------- .../Controllers/CslaStateController.cs | 21 +++++++++++++++--- .../ProjectTracker.Blazor/Program.cs | 7 ++---- .../ProjectTracker.Blazor.csproj | 9 +++++++- .../Blazor/ApplicationContextManagerBlazor.cs | 2 +- 12 files changed, 46 insertions(+), 85 deletions(-) delete mode 100644 Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor.Client/ActiveCircuitState.cs delete mode 100644 Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor.Client/RenderModeProvider.cs delete mode 100644 Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor.Client/RenderModes.cs delete mode 100644 Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor/ActiveCircuitHandler.cs diff --git a/Samples/ProjectTracker/Directory.Packages.props b/Samples/ProjectTracker/Directory.Packages.props index 5a7393efcc..4f319f00dc 100644 --- a/Samples/ProjectTracker/Directory.Packages.props +++ b/Samples/ProjectTracker/Directory.Packages.props @@ -1,13 +1,15 @@ true - 9.0.0-alpha-g3aa97cd7dd + 9.0.0-preview-g9b7892aedb + + diff --git a/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor.Client/ActiveCircuitState.cs b/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor.Client/ActiveCircuitState.cs deleted file mode 100644 index 128d69ec4c..0000000000 --- a/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor.Client/ActiveCircuitState.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ProjectTracker.Blazor -{ - public class ActiveCircuitState - { - public bool CircuitExists { get; set; } - } -} diff --git a/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor.Client/Pages/EditProject.razor b/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor.Client/Pages/EditProject.razor index 4fa718c3ae..4eb146f8b9 100644 --- a/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor.Client/Pages/EditProject.razor +++ b/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor.Client/Pages/EditProject.razor @@ -1,11 +1,14 @@ @page "/editproject" @page "/editproject/{id:int}" -@using Microsoft.AspNetCore.Components.Authorization + @rendermode InteractiveAuto +@using Marimer.Blazor.RenderMode +@using Microsoft.AspNetCore.Components.Authorization + @inject NavigationManager NavigationManager @inject Csla.Blazor.State.StateManager StateManager -@inject ProjectTracker.Blazor.RenderModeProvider renderModeProvider +@inject RenderModeProvider renderModeProvider @inject Csla.IDataPortal projectEditPortal @inject Csla.Blazor.ViewModel vm @inject Csla.ApplicationContext ApplicationContext @@ -17,7 +20,7 @@

@vm.Exception.ToString()

} -@if (!IsInteractive) +@if (!IsInteractive || vm.Model == null) {

Edit Project

@@ -25,11 +28,6 @@

} -else if (vm.Model == null) -{ -

Edit Project

-

Loading data...

-} else {

@vm.Model.Name

@@ -177,7 +175,7 @@ else await StateManager.InitializeAsync(); var renderMode = renderModeProvider.GetRenderMode(this); - IsInteractive = renderMode == RenderModes.WasmInteractive || renderMode == RenderModes.ServerInteractive; + IsInteractive = renderMode.IsInteractive(); if (IsInteractive) { vm.Saved += () => NavigationManager.NavigateTo("projects"); diff --git a/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor.Client/Program.cs b/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor.Client/Program.cs index 71344899f2..2fe07e3c2a 100644 --- a/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor.Client/Program.cs +++ b/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor.Client/Program.cs @@ -1,6 +1,6 @@ using Csla.Configuration; +using Marimer.Blazor.RenderMode.WebAssembly; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; -using ProjectTracker.Blazor; var builder = WebAssemblyHostBuilder.CreateDefault(args); @@ -8,16 +8,15 @@ builder.Services.AddCascadingAuthenticationState(); // Add render mode detection services -builder.Services.AddTransient(); -builder.Services.AddScoped(); +builder.Services.AddRenderModeDetection(); builder.Services.AddMemoryCache(); builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); -builder.Services.AddCsla(o => o - .AddBlazorWebAssembly(o => o.SyncContextWithServer = true) - .DataPortal(o => o.AddClientSideDataPortal(o => o - .UseHttpProxy(o => o.DataPortalUrl = "/api/dataportal")))); +builder.Services.AddCsla(_ => _ + .AddBlazorWebAssembly(_ => _.SyncContextWithServer = true) + .DataPortal(_ => _.AddClientSideDataPortal(_ => _ + .UseHttpProxy(_ => _.DataPortalUrl = "/api/dataportal")))); await builder.Build().RunAsync(); diff --git a/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor.Client/ProjectTracker.Blazor.Client.csproj b/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor.Client/ProjectTracker.Blazor.Client.csproj index 540cf002f6..4e5f2635df 100644 --- a/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor.Client/ProjectTracker.Blazor.Client.csproj +++ b/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor.Client/ProjectTracker.Blazor.Client.csproj @@ -10,6 +10,7 @@ + diff --git a/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor.Client/RenderModeProvider.cs b/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor.Client/RenderModeProvider.cs deleted file mode 100644 index 76ae996c2e..0000000000 --- a/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor.Client/RenderModeProvider.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Microsoft.AspNetCore.Components; - -namespace ProjectTracker.Blazor -{ - public class RenderModeProvider(ActiveCircuitState activeCircuitState) - { - public RenderModes GetRenderMode(ComponentBase page) - { - RenderModes result; - var isBrowser = OperatingSystem.IsBrowser(); - if (isBrowser) - result = RenderModes.WasmInteractive; - else if (activeCircuitState.CircuitExists) - result = RenderModes.ServerInteractive; - else if (page.GetType().GetCustomAttributes(typeof(StreamRenderingAttribute), true).Length > 0) - result = RenderModes.ServerStaticStreaming; - else - result = RenderModes.ServerStatic; - return result; - } - } -} diff --git a/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor.Client/RenderModes.cs b/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor.Client/RenderModes.cs deleted file mode 100644 index 4e86c818fc..0000000000 --- a/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor.Client/RenderModes.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace ProjectTracker.Blazor -{ - public enum RenderModes - { - WasmInteractive, - ServerInteractive, - ServerStatic, - ServerStaticStreaming - } -} diff --git a/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor/ActiveCircuitHandler.cs b/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor/ActiveCircuitHandler.cs deleted file mode 100644 index 350d925b8e..0000000000 --- a/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor/ActiveCircuitHandler.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Microsoft.AspNetCore.Components.Server.Circuits; - -namespace ProjectTracker.Blazor -{ - public class ActiveCircuitHandler(ActiveCircuitState state) : CircuitHandler - { - public override Task OnCircuitOpenedAsync(Circuit circuit, CancellationToken cancellationToken) - { - state.CircuitExists = true; - return base.OnCircuitOpenedAsync(circuit, cancellationToken); - } - - public override Task OnCircuitClosedAsync(Circuit circuit, CancellationToken cancellationToken) - { - state.CircuitExists = false; - return base.OnCircuitClosedAsync(circuit, cancellationToken); - } - } -} diff --git a/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor/Controllers/CslaStateController.cs b/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor/Controllers/CslaStateController.cs index b483563bc7..22673a89fd 100644 --- a/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor/Controllers/CslaStateController.cs +++ b/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor/Controllers/CslaStateController.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Mvc; using Csla; using Csla.State; +using Csla.Blazor.State.Messages; namespace ProjectTracker.Blazor.Controllers { @@ -12,7 +13,21 @@ namespace ProjectTracker.Blazor.Controllers /// [ApiController] [Route("[controller]")] - public class CslaStateController(ApplicationContext applicationContext, ISessionManager sessionManager) : - Csla.AspNetCore.Blazor.State.StateController(applicationContext, sessionManager) - { } + public class CslaStateController : Csla.AspNetCore.Blazor.State.StateController + { + public CslaStateController(ApplicationContext applicationContext, ISessionManager sessionManager) : + base(applicationContext, sessionManager) + { + ApplicationContext = applicationContext; + } + + private ApplicationContext ApplicationContext { get; set; } + + public override StateResult Get(long lastTouched) + { + var user = ApplicationContext.User; + var result = base.Get(lastTouched); + return result; + } + } } \ No newline at end of file diff --git a/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor/Program.cs b/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor/Program.cs index d3eb762a84..5df92d14a1 100644 --- a/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor/Program.cs +++ b/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor/Program.cs @@ -1,7 +1,6 @@ using Csla.Configuration; +using Marimer.Blazor.RenderMode; using Microsoft.AspNetCore.Authentication.Cookies; -using Microsoft.AspNetCore.Components.Server.Circuits; -using ProjectTracker.Blazor; using ProjectTracker.Blazor.Components; using ProjectTracker.Configuration; @@ -20,9 +19,7 @@ builder.Services.AddCascadingAuthenticationState(); // Add render mode detection services -builder.Services.AddTransient(); -builder.Services.AddScoped(); -builder.Services.AddScoped(typeof(CircuitHandler), typeof(ActiveCircuitHandler)); +builder.Services.AddRenderModeDetection(); // CSLA requires AddHttpContextAccessor builder.Services.AddHttpContextAccessor(); diff --git a/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor/ProjectTracker.Blazor.csproj b/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor/ProjectTracker.Blazor.csproj index 71e49adf54..16966568f6 100644 --- a/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor/ProjectTracker.Blazor.csproj +++ b/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor/ProjectTracker.Blazor.csproj @@ -6,19 +6,26 @@ enable + + + + + + + + - diff --git a/Source/Csla.AspNetCore/Blazor/ApplicationContextManagerBlazor.cs b/Source/Csla.AspNetCore/Blazor/ApplicationContextManagerBlazor.cs index d87ebf2dfa..7afc2db75b 100644 --- a/Source/Csla.AspNetCore/Blazor/ApplicationContextManagerBlazor.cs +++ b/Source/Csla.AspNetCore/Blazor/ApplicationContextManagerBlazor.cs @@ -137,7 +137,7 @@ public IPrincipal GetUser() /// Not supported in Blazor. /// /// Principal object. - public virtual void SetUser(IPrincipal principal) => throw new NotSupportedException(nameof(SetUser)); + public virtual void SetUser(IPrincipal principal) { /* noop */ } // => throw new NotSupportedException(nameof(SetUser)); /// /// Gets the local context. From 9607d510e577fd8d60c78411a815e471c33c4cdf Mon Sep 17 00:00:00 2001 From: Rockford Lhotka Date: Tue, 1 Oct 2024 17:54:32 -0500 Subject: [PATCH 03/10] #4182 Fix Blazor context manager issue --- .../Blazor/ApplicationContextManagerBlazor.cs | 2 +- .../AppContext/ContextManagerTests.cs | 20 ++++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/Source/Csla.AspNetCore/Blazor/ApplicationContextManagerBlazor.cs b/Source/Csla.AspNetCore/Blazor/ApplicationContextManagerBlazor.cs index 7afc2db75b..2bb38a8a64 100644 --- a/Source/Csla.AspNetCore/Blazor/ApplicationContextManagerBlazor.cs +++ b/Source/Csla.AspNetCore/Blazor/ApplicationContextManagerBlazor.cs @@ -116,7 +116,7 @@ private void AuthenticationStateProvider_AuthenticationStateChanged(Task public bool IsValid { - get { return HttpContext is not null || ActiveCircuitState.CircuitExists; } + get { return ActiveCircuitState.CircuitExists; } } /// diff --git a/Source/Csla.Blazor.Test/AppContext/ContextManagerTests.cs b/Source/Csla.Blazor.Test/AppContext/ContextManagerTests.cs index b07675e9b1..6dffd3fa7f 100644 --- a/Source/Csla.Blazor.Test/AppContext/ContextManagerTests.cs +++ b/Source/Csla.Blazor.Test/AppContext/ContextManagerTests.cs @@ -70,12 +70,30 @@ public void UseBlazorApplicationContextManager() var serviceProvider = services.BuildServiceProvider(); var activeState = serviceProvider.GetRequiredService(); - activeState.CircuitExists = false; + activeState.CircuitExists = true; var applicationContext = serviceProvider.GetRequiredService(); Assert.IsInstanceOfType(applicationContext.ContextManager, typeof(Csla.AspNetCore.Blazor.ApplicationContextManagerBlazor)); } + [TestMethod] + public void UseAspNetCoreOverBlazorApplicationContextManager() + { + var services = new ServiceCollection(); + services.AddScoped(); + services.AddScoped(); + services.AddCsla(o => o + .AddAspNetCore() + .AddServerSideBlazor(o => o.UseInMemoryApplicationContextManager = false)); + var serviceProvider = services.BuildServiceProvider(); + + var activeState = serviceProvider.GetRequiredService(); + activeState.CircuitExists = false; + + var applicationContext = serviceProvider.GetRequiredService(); + Assert.IsInstanceOfType(applicationContext.ContextManager, typeof(Csla.AspNetCore.ApplicationContextManagerHttpContext)); + } + [TestMethod] public void UseAspNetCoreApplicationContextManager() { From e211fde2700d1f6ec39f816674cad0fb1b27d281 Mon Sep 17 00:00:00 2001 From: Rockford Lhotka Date: Tue, 1 Oct 2024 18:09:28 -0500 Subject: [PATCH 04/10] Optimize code --- Source/Csla.AspNetCore/Blazor/State/SessionIdManager.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Csla.AspNetCore/Blazor/State/SessionIdManager.cs b/Source/Csla.AspNetCore/Blazor/State/SessionIdManager.cs index a0ce34ae54..59ac199f29 100644 --- a/Source/Csla.AspNetCore/Blazor/State/SessionIdManager.cs +++ b/Source/Csla.AspNetCore/Blazor/State/SessionIdManager.cs @@ -39,13 +39,13 @@ public string GetSessionId() if (httpContext == null) throw new InvalidOperationException("HttpContext == null"); - if (httpContext.Request.Cookies.ContainsKey(sessionIdName)) + if (httpContext.Request.Cookies.TryGetValue(sessionIdName, out var requestItem)) { - result = httpContext.Request.Cookies[sessionIdName]; + result = requestItem; } - else if (httpContext.Items.TryGetValue(sessionIdName, out var item)) + else if (httpContext.Items.TryGetValue(sessionIdName, out var itemsItem)) { - result = item as string; + result = itemsItem as string; } else { From ea9798262332501e1334e9a33387d1079ce7c5ad Mon Sep 17 00:00:00 2001 From: Rockford Lhotka Date: Tue, 1 Oct 2024 18:52:30 -0500 Subject: [PATCH 05/10] Fix issue where "non-mobile" objects don't serialize Non-mobile objects now need to have an opportunity to be serialized by a custom formatter for that type, so all types should _try_ to serialize. --- Source/Csla/Core/FieldManager/FieldDataManager.cs | 6 ++++-- .../Csla/Serialization/Mobile/MobileFormatter.cs | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/Source/Csla/Core/FieldManager/FieldDataManager.cs b/Source/Csla/Core/FieldManager/FieldDataManager.cs index d4cf0061d9..35a44b9ce2 100644 --- a/Source/Csla/Core/FieldManager/FieldDataManager.cs +++ b/Source/Csla/Core/FieldManager/FieldDataManager.cs @@ -15,6 +15,7 @@ using Csla.Properties; using Csla.Serialization; using Csla.Serialization.Mobile; +using System.Diagnostics; namespace Csla.Core.FieldManager { @@ -741,9 +742,10 @@ protected override void OnGetChildren(SerializationInfo info, MobileFormatter fo { foreach (IFieldData data in _fieldData) { - if (data?.Value is IMobileObject mobile) + var serializable = formatter.IsTypeSerializable(data?.Value?.GetType()); + if (serializable) { - SerializationInfo childInfo = formatter.SerializeObject(mobile); + SerializationInfo childInfo = formatter.SerializeObject(data?.Value); info.AddChild(data.Name, childInfo.ReferenceId, data.IsDirty); } } diff --git a/Source/Csla/Serialization/Mobile/MobileFormatter.cs b/Source/Csla/Serialization/Mobile/MobileFormatter.cs index b5d4b84a94..6c61a388a9 100644 --- a/Source/Csla/Serialization/Mobile/MobileFormatter.cs +++ b/Source/Csla/Serialization/Mobile/MobileFormatter.cs @@ -6,6 +6,7 @@ // Serializes and deserializes objects //----------------------------------------------------------------------- +using System.ComponentModel.DataAnnotations; using Csla.Configuration; using Csla.Properties; using Csla.Reflection; @@ -397,7 +398,19 @@ public object DeserializeFromSerializationInfo(List data) return DeserializeAsDTO(data); } -#endregion + #endregion + /// + /// Determines whether the specified type can be serialized as a child. + /// + /// The type to check. + /// + public bool IsTypeSerializable(Type type) + { + if (typeof(IMobileObject).IsAssignableFrom(type)) + return true; + var options = GetOptions(); + return options.CustomSerializers.Any(s => s.CanSerialize(type)); + } } } \ No newline at end of file From 879648c1596498f715fdc9ed7c71c572fcd7e46d Mon Sep 17 00:00:00 2001 From: Rockford Lhotka Date: Tue, 1 Oct 2024 23:18:14 -0500 Subject: [PATCH 06/10] Clarify test names --- .../AppContext/ContextManagerTests.cs | 23 +++---------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/Source/Csla.Blazor.Test/AppContext/ContextManagerTests.cs b/Source/Csla.Blazor.Test/AppContext/ContextManagerTests.cs index 6dffd3fa7f..a1a7947e67 100644 --- a/Source/Csla.Blazor.Test/AppContext/ContextManagerTests.cs +++ b/Source/Csla.Blazor.Test/AppContext/ContextManagerTests.cs @@ -41,7 +41,7 @@ public void UseApplicationContextManagerInMemory() } [TestMethod] - public void UseApplicationContextManagerBlazor() + public void NoHttpContextAndCircuitExist_UseBlazor() { var services = new ServiceCollection(); services.AddScoped(); @@ -59,7 +59,7 @@ public void UseApplicationContextManagerBlazor() } [TestMethod] - public void UseBlazorApplicationContextManager() + public void HttpContextAndCircuitExist_UseBlazor() { var services = new ServiceCollection(); services.AddScoped(); @@ -77,7 +77,7 @@ public void UseBlazorApplicationContextManager() } [TestMethod] - public void UseAspNetCoreOverBlazorApplicationContextManager() + public void HttpContextAndNoCircuit_UseHttpContext() { var services = new ServiceCollection(); services.AddScoped(); @@ -93,23 +93,6 @@ public void UseAspNetCoreOverBlazorApplicationContextManager() var applicationContext = serviceProvider.GetRequiredService(); Assert.IsInstanceOfType(applicationContext.ContextManager, typeof(Csla.AspNetCore.ApplicationContextManagerHttpContext)); } - - [TestMethod] - public void UseAspNetCoreApplicationContextManager() - { - var services = new ServiceCollection(); - services.AddScoped(); - services.AddScoped(); - services.AddCsla(o => o - .AddAspNetCore()); - var serviceProvider = services.BuildServiceProvider(); - - var activeState = serviceProvider.GetRequiredService(); - activeState.CircuitExists = false; - - var applicationContext = serviceProvider.GetRequiredService(); - Assert.IsInstanceOfType(applicationContext.ContextManager, typeof(Csla.AspNetCore.ApplicationContextManagerHttpContext)); - } } From c9290534f66d7b5e56092f3b7d2696f028208f3f Mon Sep 17 00:00:00 2001 From: Rockford Lhotka Date: Tue, 1 Oct 2024 23:19:24 -0500 Subject: [PATCH 07/10] #4182 Fix context manager accessor --- .../Blazor/ApplicationContextManagerBlazor.cs | 36 ++++++++++++++----- .../Blazor/State/SessionIdManager.cs | 3 +- .../Csla.test/AppContext/AppContextTests.cs | 2 +- .../Csla/Core/ApplicationContextAccessor.cs | 9 ++--- 4 files changed, 33 insertions(+), 17 deletions(-) diff --git a/Source/Csla.AspNetCore/Blazor/ApplicationContextManagerBlazor.cs b/Source/Csla.AspNetCore/Blazor/ApplicationContextManagerBlazor.cs index 2bb38a8a64..5ac4ef19a5 100644 --- a/Source/Csla.AspNetCore/Blazor/ApplicationContextManagerBlazor.cs +++ b/Source/Csla.AspNetCore/Blazor/ApplicationContextManagerBlazor.cs @@ -116,7 +116,11 @@ private void AuthenticationStateProvider_AuthenticationStateChanged(Task public bool IsValid { - get { return ActiveCircuitState.CircuitExists; } + get + { + var result = ActiveCircuitState.CircuitExists; + return result; + } } /// @@ -137,19 +141,33 @@ public IPrincipal GetUser() /// Not supported in Blazor. /// /// Principal object. - public virtual void SetUser(IPrincipal principal) { /* noop */ } // => throw new NotSupportedException(nameof(SetUser)); + public virtual void SetUser(IPrincipal principal) + { + ArgumentNullException.ThrowIfNull(principal); + if (HttpContext != null) + { + HttpContext.User = (ClaimsPrincipal)principal; + } + else + { + throw new NotSupportedException(nameof(SetUser)); + } + } + + private const string _localContextName = "Csla.LocalContext"; + private const string _clientContextName = "Csla.ClientContext"; /// /// Gets the local context. /// /// is . - public IContextDictionary GetLocalContext() + public IContextDictionary? GetLocalContext() { ThrowIfApplicationContextIsNull(); - IContextDictionary localContext; + IContextDictionary? localContext; var sessionManager = ApplicationContext.GetRequiredService(); var session = sessionManager.GetSession(); - session.TryGetValue("localContext", out var result); + session.TryGetValue(_localContextName, out var result); if (result is IContextDictionary context) { localContext = context; @@ -172,7 +190,7 @@ public void SetLocalContext(IContextDictionary? localContext) ThrowIfApplicationContextIsNull(); var sessionManager = ApplicationContext.GetRequiredService(); var session = sessionManager.GetSession(); - session["localContext"] = localContext; + session[_localContextName] = localContext; } /// @@ -186,7 +204,7 @@ public IContextDictionary GetClientContext(ApplicationContext.ExecutionLocations IContextDictionary clientContext; var sessionManager = ApplicationContext.GetRequiredService(); var session = sessionManager.GetSession(); - session.TryGetValue("clientContext", out var result); + session.TryGetValue(_clientContextName, out var result); if (result is IContextDictionary context) { clientContext = context; @@ -202,7 +220,7 @@ public IContextDictionary GetClientContext(ApplicationContext.ExecutionLocations /// /// Sets the client context. /// - /// Client context. + /// /// /// is . public void SetClientContext(IContextDictionary? clientContext, ApplicationContext.ExecutionLocations executionLocation) @@ -210,7 +228,7 @@ public void SetClientContext(IContextDictionary? clientContext, ApplicationConte ThrowIfApplicationContextIsNull(); var sessionManager = ApplicationContext.GetRequiredService(); var session = sessionManager.GetSession(); - session["clientContext"] = clientContext; + session[_clientContextName] = clientContext; } /// diff --git a/Source/Csla.AspNetCore/Blazor/State/SessionIdManager.cs b/Source/Csla.AspNetCore/Blazor/State/SessionIdManager.cs index 59ac199f29..2a8fdb239f 100644 --- a/Source/Csla.AspNetCore/Blazor/State/SessionIdManager.cs +++ b/Source/Csla.AspNetCore/Blazor/State/SessionIdManager.cs @@ -50,8 +50,9 @@ public string GetSessionId() else { result = Guid.NewGuid().ToString(); - httpContext.Response.Cookies.Append(sessionIdName, result); httpContext.Items[sessionIdName] = result; + if (!httpContext.Response.HasStarted) + httpContext.Response.Cookies.Append(sessionIdName, result); } return result ?? throw new InvalidOperationException(Csla.Properties.Resources.SessionIdManagerIdMustBeNotNull); diff --git a/Source/Csla.test/AppContext/AppContextTests.cs b/Source/Csla.test/AppContext/AppContextTests.cs index c6b252933e..a5011811c3 100644 --- a/Source/Csla.test/AppContext/AppContextTests.cs +++ b/Source/Csla.test/AppContext/AppContextTests.cs @@ -34,7 +34,6 @@ public void Initialize() } [TestMethod] - [ExpectedException(typeof(InvalidOperationException))] public void UseDefaultApplicationContextManager() { var services = new ServiceCollection(); @@ -45,6 +44,7 @@ public void UseDefaultApplicationContextManager() var serviceProvider = services.BuildServiceProvider(); var applicationContext = serviceProvider.GetRequiredService(); + Assert.IsInstanceOfType(applicationContext.ContextManager, typeof(ApplicationContextManagerAsyncLocal)); } [TestMethod] diff --git a/Source/Csla/Core/ApplicationContextAccessor.cs b/Source/Csla/Core/ApplicationContextAccessor.cs index e7bcec4284..9d091b1a62 100644 --- a/Source/Csla/Core/ApplicationContextAccessor.cs +++ b/Source/Csla/Core/ApplicationContextAccessor.cs @@ -5,6 +5,7 @@ // // Provides access to the correct current application //----------------------------------------------------------------------- +#nullable enable using Csla.Runtime; using Microsoft.Extensions.DependencyInjection; @@ -40,14 +41,10 @@ public ApplicationContextAccessor( break; } } - if (ContextManager is null) - { - throw new InvalidOperationException("ContextManager == null"); - } } internal IServiceProvider ServiceProvider { get; } - private IContextManager ContextManager { get; set; } + private IContextManager? ContextManager { get; set; } private IContextManager LocalContextManager { get; set; } /// @@ -57,7 +54,7 @@ public ApplicationContextAccessor( public IContextManager GetContextManager() { var runtimeInfo = ServiceProvider.GetRequiredService(); - if (!runtimeInfo.LocalProxyNewScopeExists) + if (ContextManager != null && !runtimeInfo.LocalProxyNewScopeExists) return ContextManager; else return LocalContextManager; From 0467c7b88b0e0e6c67886470c7acf97e02ed4a4a Mon Sep 17 00:00:00 2001 From: Rockford Lhotka Date: Tue, 1 Oct 2024 23:24:20 -0500 Subject: [PATCH 08/10] Update to latest CSLA --- .../ProjectTracker/Directory.Packages.props | 2 +- .../Controllers/CslaStateController.cs | 21 +++---------------- 2 files changed, 4 insertions(+), 19 deletions(-) diff --git a/Samples/ProjectTracker/Directory.Packages.props b/Samples/ProjectTracker/Directory.Packages.props index 4f319f00dc..72cbf55eb1 100644 --- a/Samples/ProjectTracker/Directory.Packages.props +++ b/Samples/ProjectTracker/Directory.Packages.props @@ -1,7 +1,7 @@ true - 9.0.0-preview-g9b7892aedb + 9.0.0-preview-gc9290534f6 diff --git a/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor/Controllers/CslaStateController.cs b/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor/Controllers/CslaStateController.cs index 22673a89fd..eaddf485ae 100644 --- a/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor/Controllers/CslaStateController.cs +++ b/Samples/ProjectTracker/ProjectTracker.Blazor/ProjectTracker.Blazor/Controllers/CslaStateController.cs @@ -1,7 +1,6 @@ using Microsoft.AspNetCore.Mvc; using Csla; using Csla.State; -using Csla.Blazor.State.Messages; namespace ProjectTracker.Blazor.Controllers { @@ -13,21 +12,7 @@ namespace ProjectTracker.Blazor.Controllers /// [ApiController] [Route("[controller]")] - public class CslaStateController : Csla.AspNetCore.Blazor.State.StateController - { - public CslaStateController(ApplicationContext applicationContext, ISessionManager sessionManager) : - base(applicationContext, sessionManager) - { - ApplicationContext = applicationContext; - } - - private ApplicationContext ApplicationContext { get; set; } - - public override StateResult Get(long lastTouched) - { - var user = ApplicationContext.User; - var result = base.Get(lastTouched); - return result; - } - } + public class CslaStateController(ApplicationContext applicationContext, ISessionManager sessionManager) : + Csla.AspNetCore.Blazor.State.StateController(applicationContext, sessionManager) + { } } \ No newline at end of file From 24aea28be03ea4fb1d0ed5d28efa69ab4e1af502 Mon Sep 17 00:00:00 2001 From: Rockford Lhotka Date: Mon, 21 Oct 2024 13:29:14 -0500 Subject: [PATCH 09/10] #4182 Fix issue with IContextManager selection --- .../Blazor/ApplicationContextManagerBlazor.cs | 9 +--- .../AppContext/ContextManagerTests.cs | 49 ++++++++++++++++--- 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/Source/Csla.AspNetCore/Blazor/ApplicationContextManagerBlazor.cs b/Source/Csla.AspNetCore/Blazor/ApplicationContextManagerBlazor.cs index 5ac4ef19a5..8e9f5f0372 100644 --- a/Source/Csla.AspNetCore/Blazor/ApplicationContextManagerBlazor.cs +++ b/Source/Csla.AspNetCore/Blazor/ApplicationContextManagerBlazor.cs @@ -114,14 +114,7 @@ private void AuthenticationStateProvider_AuthenticationStateChanged(Task - public bool IsValid - { - get - { - var result = ActiveCircuitState.CircuitExists; - return result; - } - } + public bool IsValid => ActiveCircuitState.CircuitExists; /// /// Gets a value indicating whether the context manager diff --git a/Source/Csla.Blazor.Test/AppContext/ContextManagerTests.cs b/Source/Csla.Blazor.Test/AppContext/ContextManagerTests.cs index a1a7947e67..1640678b22 100644 --- a/Source/Csla.Blazor.Test/AppContext/ContextManagerTests.cs +++ b/Source/Csla.Blazor.Test/AppContext/ContextManagerTests.cs @@ -41,7 +41,7 @@ public void UseApplicationContextManagerInMemory() } [TestMethod] - public void NoHttpContextAndCircuitExist_UseBlazor() + public void UseApplicationContextManagerBlazor() { var services = new ServiceCollection(); services.AddScoped(); @@ -59,7 +59,25 @@ public void NoHttpContextAndCircuitExist_UseBlazor() } [TestMethod] - public void HttpContextAndCircuitExist_UseBlazor() + public void UseAsyncLocalApplicationContextManager() + { + var services = new ServiceCollection(); + services.AddScoped(); + services.AddHttpContextAccessor(); + services.AddCsla(o => o + .AddAspNetCore() + .AddServerSideBlazor(o => o.UseInMemoryApplicationContextManager = false)); + var serviceProvider = services.BuildServiceProvider(); + + var activeState = serviceProvider.GetRequiredService(); + activeState.CircuitExists = false; + + var applicationContext = serviceProvider.GetRequiredService(); + Assert.IsInstanceOfType(applicationContext.ContextManager, typeof(Csla.Core.ApplicationContextManagerAsyncLocal)); + } + + [TestMethod] + public void UseBlazorApplicationContextManager() { var services = new ServiceCollection(); services.AddScoped(); @@ -77,7 +95,7 @@ public void HttpContextAndCircuitExist_UseBlazor() } [TestMethod] - public void HttpContextAndNoCircuit_UseHttpContext() + public void UseAspNetCoreOverBlazorApplicationContextManager() { var services = new ServiceCollection(); services.AddScoped(); @@ -93,6 +111,23 @@ public void HttpContextAndNoCircuit_UseHttpContext() var applicationContext = serviceProvider.GetRequiredService(); Assert.IsInstanceOfType(applicationContext.ContextManager, typeof(Csla.AspNetCore.ApplicationContextManagerHttpContext)); } + + [TestMethod] + public void UseAspNetCoreApplicationContextManager() + { + var services = new ServiceCollection(); + services.AddScoped(); + services.AddScoped(); + services.AddCsla(o => o + .AddAspNetCore()); + var serviceProvider = services.BuildServiceProvider(); + + var activeState = serviceProvider.GetRequiredService(); + activeState.CircuitExists = false; + + var applicationContext = serviceProvider.GetRequiredService(); + Assert.IsInstanceOfType(applicationContext.ContextManager, typeof(Csla.AspNetCore.ApplicationContextManagerHttpContext)); + } } @@ -104,12 +139,12 @@ public override Task GetAuthenticationStateAsync() } } -public class HttpContextAccessorFake : Microsoft.AspNetCore.Http.IHttpContextAccessor +public class HttpContextAccessorFake : IHttpContextAccessor { - public Microsoft.AspNetCore.Http.HttpContext HttpContext { get; set; } = new HttpContextFake(); + public HttpContext HttpContext { get; set; } = new HttpContextFake(); } -public class HttpContextFake : Microsoft.AspNetCore.Http.HttpContext +public class HttpContextFake : HttpContext { public override IFeatureCollection Features => throw new NotImplementedException(); @@ -129,4 +164,4 @@ public class HttpContextFake : Microsoft.AspNetCore.Http.HttpContext public override ISession Session { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } public override void Abort() => throw new NotImplementedException(); -} +} \ No newline at end of file From 9fa0dcb86ead3e7f90e8412bbcb11ce08838dcad Mon Sep 17 00:00:00 2001 From: Rockford Lhotka Date: Mon, 21 Oct 2024 13:38:36 -0500 Subject: [PATCH 10/10] Provide better exception information --- .../Blazor/ApplicationContextManagerBlazor.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Source/Csla.AspNetCore/Blazor/ApplicationContextManagerBlazor.cs b/Source/Csla.AspNetCore/Blazor/ApplicationContextManagerBlazor.cs index 8e9f5f0372..58bae04715 100644 --- a/Source/Csla.AspNetCore/Blazor/ApplicationContextManagerBlazor.cs +++ b/Source/Csla.AspNetCore/Blazor/ApplicationContextManagerBlazor.cs @@ -139,11 +139,14 @@ public virtual void SetUser(IPrincipal principal) ArgumentNullException.ThrowIfNull(principal); if (HttpContext != null) { - HttpContext.User = (ClaimsPrincipal)principal; + if (principal is ClaimsPrincipal claimsPrincipal) + HttpContext.User = claimsPrincipal; + else + throw new NotSupportedException($"{nameof(SetUser)}: {nameof(principal)}.GetType() != {nameof(ClaimsPrincipal)}"); } else { - throw new NotSupportedException(nameof(SetUser)); + throw new NotSupportedException($"{nameof(SetUser)}: {nameof(HttpContext)} == null"); } }