Added SessionState Components
| 
						 | 
					@ -1,7 +1,48 @@
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  "Version": 1,
 | 
					  "Version": 1,
 | 
				
			||||||
  "WorkspaceRootPath": "C:\\Repos\\SummerBestWebForm2\\",
 | 
					  "WorkspaceRootPath": "C:\\Repos\\SummerBestWebForm2\\",
 | 
				
			||||||
  "Documents": [],
 | 
					  "Documents": [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "AbsoluteMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|c:\\repos\\summerbestwebform2\\components\\app.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
 | 
				
			||||||
 | 
					      "RelativeMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|solutionrelative:components\\app.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "AbsoluteMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|c:\\repos\\summerbestwebform2\\components\\_imports.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
 | 
				
			||||||
 | 
					      "RelativeMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|solutionrelative:components\\_imports.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "AbsoluteMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|c:\\repos\\summerbestwebform2\\sessionstate\\sessionmanager.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
 | 
				
			||||||
 | 
					      "RelativeMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|solutionrelative:sessionstate\\sessionmanager.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "AbsoluteMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|c:\\repos\\summerbestwebform2\\sessionstate\\sessionidmanager.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
 | 
				
			||||||
 | 
					      "RelativeMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|solutionrelative:sessionstate\\sessionidmanager.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "AbsoluteMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|c:\\repos\\summerbestwebform2\\classobj\\mdlsessioninfo.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
 | 
				
			||||||
 | 
					      "RelativeMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|solutionrelative:classobj\\mdlsessioninfo.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "AbsoluteMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|c:\\repos\\summerbestwebform2\\sessionstate\\mdlsession.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
 | 
				
			||||||
 | 
					      "RelativeMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|solutionrelative:sessionstate\\mdlsession.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "AbsoluteMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|c:\\repos\\summerbestwebform2\\appstate\\mdlappstate.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
 | 
				
			||||||
 | 
					      "RelativeMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|solutionrelative:appstate\\mdlappstate.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "AbsoluteMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|c:\\repos\\summerbestwebform2\\appstate\\cascadingappstate.razor.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
 | 
				
			||||||
 | 
					      "RelativeMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|solutionrelative:appstate\\cascadingappstate.razor.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "AbsoluteMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|c:\\repos\\summerbestwebform2\\appstate\\iappstate.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
 | 
				
			||||||
 | 
					      "RelativeMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|solutionrelative:appstate\\iappstate.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "AbsoluteMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|c:\\repos\\summerbestwebform2\\program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
 | 
				
			||||||
 | 
					      "RelativeMoniker": "D:0:0:{10E16044-8880-42A4-866B-B0461C450A71}|SummerBestWebForm2.csproj|solutionrelative:program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
  "DocumentGroupContainers": [
 | 
					  "DocumentGroupContainers": [
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      "Orientation": 0,
 | 
					      "Orientation": 0,
 | 
				
			||||||
| 
						 | 
					@ -9,7 +50,7 @@
 | 
				
			||||||
      "DocumentGroups": [
 | 
					      "DocumentGroups": [
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
          "DockedWidth": 200,
 | 
					          "DockedWidth": 200,
 | 
				
			||||||
          "SelectedChildIndex": -1,
 | 
					          "SelectedChildIndex": 11,
 | 
				
			||||||
          "Children": [
 | 
					          "Children": [
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
              "$type": "Bookmark",
 | 
					              "$type": "Bookmark",
 | 
				
			||||||
| 
						 | 
					@ -54,6 +95,136 @@
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
              "$type": "Bookmark",
 | 
					              "$type": "Bookmark",
 | 
				
			||||||
              "Name": "ST:0:0:{aa2115a1-9712-457b-9047-dbb71ca2cdd2}"
 | 
					              "Name": "ST:0:0:{aa2115a1-9712-457b-9047-dbb71ca2cdd2}"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              "$type": "Document",
 | 
				
			||||||
 | 
					              "DocumentIndex": 0,
 | 
				
			||||||
 | 
					              "Title": "App.razor",
 | 
				
			||||||
 | 
					              "DocumentMoniker": "C:\\Repos\\SummerBestWebForm2\\Components\\App.razor",
 | 
				
			||||||
 | 
					              "RelativeDocumentMoniker": "Components\\App.razor",
 | 
				
			||||||
 | 
					              "ToolTip": "C:\\Repos\\SummerBestWebForm2\\Components\\App.razor",
 | 
				
			||||||
 | 
					              "RelativeToolTip": "Components\\App.razor",
 | 
				
			||||||
 | 
					              "ViewState": "AgIAACQAAAAAAAAAAAAYwFwAAAAAAAAAAAAAAA==",
 | 
				
			||||||
 | 
					              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
 | 
				
			||||||
 | 
					              "WhenOpened": "2025-05-15T09:23:12.142Z",
 | 
				
			||||||
 | 
					              "EditorCaption": ""
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              "$type": "Document",
 | 
				
			||||||
 | 
					              "DocumentIndex": 9,
 | 
				
			||||||
 | 
					              "Title": "Program.cs",
 | 
				
			||||||
 | 
					              "DocumentMoniker": "C:\\Repos\\SummerBestWebForm2\\Program.cs",
 | 
				
			||||||
 | 
					              "RelativeDocumentMoniker": "Program.cs",
 | 
				
			||||||
 | 
					              "ToolTip": "C:\\Repos\\SummerBestWebForm2\\Program.cs",
 | 
				
			||||||
 | 
					              "RelativeToolTip": "Program.cs",
 | 
				
			||||||
 | 
					              "ViewState": "AgIAABMAAAAAAAAAAAAAACsAAAAAAAAAAAAAAA==",
 | 
				
			||||||
 | 
					              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
 | 
				
			||||||
 | 
					              "WhenOpened": "2025-05-15T10:01:38.124Z",
 | 
				
			||||||
 | 
					              "EditorCaption": ""
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              "$type": "Document",
 | 
				
			||||||
 | 
					              "DocumentIndex": 8,
 | 
				
			||||||
 | 
					              "Title": "IAppState.cs",
 | 
				
			||||||
 | 
					              "DocumentMoniker": "C:\\Repos\\SummerBestWebForm2\\AppState\\IAppState.cs",
 | 
				
			||||||
 | 
					              "RelativeDocumentMoniker": "AppState\\IAppState.cs",
 | 
				
			||||||
 | 
					              "ToolTip": "C:\\Repos\\SummerBestWebForm2\\AppState\\IAppState.cs",
 | 
				
			||||||
 | 
					              "RelativeToolTip": "AppState\\IAppState.cs",
 | 
				
			||||||
 | 
					              "ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAAAAAA==",
 | 
				
			||||||
 | 
					              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
 | 
				
			||||||
 | 
					              "WhenOpened": "2025-05-15T10:03:35.861Z",
 | 
				
			||||||
 | 
					              "EditorCaption": ""
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              "$type": "Document",
 | 
				
			||||||
 | 
					              "DocumentIndex": 7,
 | 
				
			||||||
 | 
					              "Title": "CascadingAppState.razor.cs",
 | 
				
			||||||
 | 
					              "DocumentMoniker": "C:\\Repos\\SummerBestWebForm2\\AppState\\CascadingAppState.razor.cs",
 | 
				
			||||||
 | 
					              "RelativeDocumentMoniker": "AppState\\CascadingAppState.razor.cs",
 | 
				
			||||||
 | 
					              "ToolTip": "C:\\Repos\\SummerBestWebForm2\\AppState\\CascadingAppState.razor.cs",
 | 
				
			||||||
 | 
					              "RelativeToolTip": "AppState\\CascadingAppState.razor.cs",
 | 
				
			||||||
 | 
					              "ViewState": "AgIAAAAAAAAAAAAAAAAAAAoAAAA6AAAAAAAAAA==",
 | 
				
			||||||
 | 
					              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
 | 
				
			||||||
 | 
					              "WhenOpened": "2025-05-15T10:03:51.372Z",
 | 
				
			||||||
 | 
					              "EditorCaption": ""
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              "$type": "Document",
 | 
				
			||||||
 | 
					              "DocumentIndex": 6,
 | 
				
			||||||
 | 
					              "Title": "MdlAppState.cs",
 | 
				
			||||||
 | 
					              "DocumentMoniker": "C:\\Repos\\SummerBestWebForm2\\AppState\\MdlAppState.cs",
 | 
				
			||||||
 | 
					              "RelativeDocumentMoniker": "AppState\\MdlAppState.cs",
 | 
				
			||||||
 | 
					              "ToolTip": "C:\\Repos\\SummerBestWebForm2\\AppState\\MdlAppState.cs",
 | 
				
			||||||
 | 
					              "RelativeToolTip": "AppState\\MdlAppState.cs",
 | 
				
			||||||
 | 
					              "ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAcAAAAAAAAAA==",
 | 
				
			||||||
 | 
					              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
 | 
				
			||||||
 | 
					              "WhenOpened": "2025-05-15T10:04:02.184Z",
 | 
				
			||||||
 | 
					              "EditorCaption": ""
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              "$type": "Document",
 | 
				
			||||||
 | 
					              "DocumentIndex": 5,
 | 
				
			||||||
 | 
					              "Title": "MdlSession.cs",
 | 
				
			||||||
 | 
					              "DocumentMoniker": "C:\\Repos\\SummerBestWebForm2\\SessionState\\MdlSession.cs",
 | 
				
			||||||
 | 
					              "RelativeDocumentMoniker": "SessionState\\MdlSession.cs",
 | 
				
			||||||
 | 
					              "ToolTip": "C:\\Repos\\SummerBestWebForm2\\SessionState\\MdlSession.cs",
 | 
				
			||||||
 | 
					              "RelativeToolTip": "SessionState\\MdlSession.cs",
 | 
				
			||||||
 | 
					              "ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAcAAAAAAAAAA==",
 | 
				
			||||||
 | 
					              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
 | 
				
			||||||
 | 
					              "WhenOpened": "2025-05-15T10:04:06.197Z",
 | 
				
			||||||
 | 
					              "EditorCaption": ""
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              "$type": "Document",
 | 
				
			||||||
 | 
					              "DocumentIndex": 4,
 | 
				
			||||||
 | 
					              "Title": "MdlSessionInfo.cs",
 | 
				
			||||||
 | 
					              "DocumentMoniker": "C:\\Repos\\SummerBestWebForm2\\ClassObj\\MdlSessionInfo.cs",
 | 
				
			||||||
 | 
					              "RelativeDocumentMoniker": "ClassObj\\MdlSessionInfo.cs",
 | 
				
			||||||
 | 
					              "ToolTip": "C:\\Repos\\SummerBestWebForm2\\ClassObj\\MdlSessionInfo.cs",
 | 
				
			||||||
 | 
					              "RelativeToolTip": "ClassObj\\MdlSessionInfo.cs",
 | 
				
			||||||
 | 
					              "ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAcAAAAAAAAAA==",
 | 
				
			||||||
 | 
					              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
 | 
				
			||||||
 | 
					              "WhenOpened": "2025-05-15T10:04:10.974Z",
 | 
				
			||||||
 | 
					              "EditorCaption": ""
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              "$type": "Document",
 | 
				
			||||||
 | 
					              "DocumentIndex": 3,
 | 
				
			||||||
 | 
					              "Title": "SessionIdManager.cs",
 | 
				
			||||||
 | 
					              "DocumentMoniker": "C:\\Repos\\SummerBestWebForm2\\SessionState\\SessionIdManager.cs",
 | 
				
			||||||
 | 
					              "RelativeDocumentMoniker": "SessionState\\SessionIdManager.cs",
 | 
				
			||||||
 | 
					              "ToolTip": "C:\\Repos\\SummerBestWebForm2\\SessionState\\SessionIdManager.cs",
 | 
				
			||||||
 | 
					              "RelativeToolTip": "SessionState\\SessionIdManager.cs",
 | 
				
			||||||
 | 
					              "ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAcAAAAAAAAAA==",
 | 
				
			||||||
 | 
					              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
 | 
				
			||||||
 | 
					              "WhenOpened": "2025-05-15T10:04:14.473Z",
 | 
				
			||||||
 | 
					              "EditorCaption": ""
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              "$type": "Document",
 | 
				
			||||||
 | 
					              "DocumentIndex": 2,
 | 
				
			||||||
 | 
					              "Title": "SessionManager.cs",
 | 
				
			||||||
 | 
					              "DocumentMoniker": "C:\\Repos\\SummerBestWebForm2\\SessionState\\SessionManager.cs",
 | 
				
			||||||
 | 
					              "RelativeDocumentMoniker": "SessionState\\SessionManager.cs",
 | 
				
			||||||
 | 
					              "ToolTip": "C:\\Repos\\SummerBestWebForm2\\SessionState\\SessionManager.cs",
 | 
				
			||||||
 | 
					              "RelativeToolTip": "SessionState\\SessionManager.cs",
 | 
				
			||||||
 | 
					              "ViewState": "AgIAAAIAAAAAAAAAAAAAAAIAAAAcAAAAAAAAAA==",
 | 
				
			||||||
 | 
					              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
 | 
				
			||||||
 | 
					              "WhenOpened": "2025-05-15T10:04:17.539Z",
 | 
				
			||||||
 | 
					              "EditorCaption": ""
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              "$type": "Document",
 | 
				
			||||||
 | 
					              "DocumentIndex": 1,
 | 
				
			||||||
 | 
					              "Title": "_Imports.razor",
 | 
				
			||||||
 | 
					              "DocumentMoniker": "C:\\Repos\\SummerBestWebForm2\\Components\\_Imports.razor",
 | 
				
			||||||
 | 
					              "RelativeDocumentMoniker": "Components\\_Imports.razor",
 | 
				
			||||||
 | 
					              "ToolTip": "C:\\Repos\\SummerBestWebForm2\\Components\\_Imports.razor",
 | 
				
			||||||
 | 
					              "RelativeToolTip": "Components\\_Imports.razor",
 | 
				
			||||||
 | 
					              "ViewState": "AgIAAAAAAAAAAAAAAAAAAAoAAAAiAAAAAAAAAA==",
 | 
				
			||||||
 | 
					              "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
 | 
				
			||||||
 | 
					              "WhenOpened": "2025-05-15T10:07:58.031Z",
 | 
				
			||||||
 | 
					              "EditorCaption": ""
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          ]
 | 
					          ]
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,7 @@
 | 
				
			||||||
 | 
					@* @using static Microsoft.AspNetCore.Components.Web.RenderMode
 | 
				
			||||||
 | 
					@rendermode InteractiveServer
 | 
				
			||||||
 | 
					*@
 | 
				
			||||||
 | 
					<CascadingValue Value="this">
 | 
				
			||||||
 | 
					  @ChildContent
 | 
				
			||||||
 | 
					</CascadingValue>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,154 @@
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Components;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
 | 
				
			||||||
 | 
					using System.Reflection;
 | 
				
			||||||
 | 
					using System.Text.Json;
 | 
				
			||||||
 | 
					using System.Text.RegularExpressions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace SummerBestWebForm2.AppState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public partial class CascadingAppState : ComponentBase, IAppState
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  private readonly string StorageKey = "SummerBestWebForm2-MyCarHQ";
 | 
				
			||||||
 | 
					  [Parameter]
 | 
				
			||||||
 | 
					  public RenderFragment? ChildContent { get; set; }
 | 
				
			||||||
 | 
					  [Inject]
 | 
				
			||||||
 | 
					  ProtectedLocalStorage? localStorage { get; set; }
 | 
				
			||||||
 | 
					  [Inject]
 | 
				
			||||||
 | 
					  IHttpContextAccessor? httpContextAccessor { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool isLoaded = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public Guid SessionId { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public bool isInit { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  //public CascadingAppState()
 | 
				
			||||||
 | 
					  //{
 | 
				
			||||||
 | 
					  //  Load();
 | 
				
			||||||
 | 
					  //}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private string? _myIpAddress = string.Empty;
 | 
				
			||||||
 | 
					  public string? myIpAddress
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    get => _myIpAddress;
 | 
				
			||||||
 | 
					    set
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      _myIpAddress = value;
 | 
				
			||||||
 | 
					      Save();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private DateTimeOffset _DateCreated = DateTimeOffset.Now;
 | 
				
			||||||
 | 
					  public DateTimeOffset DateCreated
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    get => _DateCreated;
 | 
				
			||||||
 | 
					    set
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      _DateCreated = value;
 | 
				
			||||||
 | 
					      Save();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  private DateTimeOffset _DateExpires = DateTimeOffset.Now;
 | 
				
			||||||
 | 
					  public DateTimeOffset DateExpires
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    get => _DateExpires;
 | 
				
			||||||
 | 
					    set
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      _DateExpires = value;
 | 
				
			||||||
 | 
					      Save();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O) / (o) (O)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  protected override async Task OnAfterRenderAsync(bool firstRender)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    await base.OnAfterRenderAsync(firstRender);
 | 
				
			||||||
 | 
					    if (firstRender)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      await LoadAsync();
 | 
				
			||||||
 | 
					      StateHasChanged();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private void Save()
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    new Task(async () =>
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      await SaveAsync();
 | 
				
			||||||
 | 
					    }).Start();
 | 
				
			||||||
 | 
					  } //Save
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public async Task SaveAsync()
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    if (!isLoaded) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // serialize 
 | 
				
			||||||
 | 
					    var state = (IAppState)this;
 | 
				
			||||||
 | 
					    var json = JsonSerializer.Serialize(state);
 | 
				
			||||||
 | 
					    // save
 | 
				
			||||||
 | 
					    await localStorage.SetAsync(StorageKey, json);
 | 
				
			||||||
 | 
					  } //SaveAsync
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private void Load()
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    new Task(async () =>
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      await LoadAsync();
 | 
				
			||||||
 | 
					    }).Start();
 | 
				
			||||||
 | 
					  } //Load
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public async Task LoadAsync()
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    string remoteIpAddr=string.Empty;
 | 
				
			||||||
 | 
					    try
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      remoteIpAddr = httpContextAccessor.HttpContext.Connection.RemoteIpAddress?.ToString() ?? "";
 | 
				
			||||||
 | 
					      var data = await localStorage.GetAsync<string>(StorageKey);
 | 
				
			||||||
 | 
					      var state = JsonSerializer.Deserialize<MdlAppState>(data.Value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (state != null)
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        if (DateTimeOffset.Now < state.DateExpires)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          // decide whether to set properties manually or with reflection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // comment to set properties manually
 | 
				
			||||||
 | 
					          //this.Message = state.Message;
 | 
				
			||||||
 | 
					          //this.Count = state.Count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // set properties using Reflaction
 | 
				
			||||||
 | 
					          var t = typeof(IAppState);
 | 
				
			||||||
 | 
					          //var props = t.GetProperties();
 | 
				
			||||||
 | 
					          PropertyInfo[] props = t.GetProperties();
 | 
				
			||||||
 | 
					          foreach (var prop in props)
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            //if (!Regex.IsMatch(prop.Name, "isInit|SessionId|"))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              var value = prop.GetValue(state, null);
 | 
				
			||||||
 | 
					              prop.SetValue(this, value, null);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    catch (Exception ex)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      // do something
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    isLoaded = true;
 | 
				
			||||||
 | 
					    DateExpires = DateTimeOffset.Now.AddHours(36);
 | 
				
			||||||
 | 
					    myIpAddress = remoteIpAddr;
 | 
				
			||||||
 | 
					    if (!isInit)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      DateCreated = DateTimeOffset.Now;
 | 
				
			||||||
 | 
					      SessionId = Guid.NewGuid();
 | 
				
			||||||
 | 
					      isInit = true;
 | 
				
			||||||
 | 
					      await SaveAsync();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    await InvokeAsync(() =>
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      StateHasChanged();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  } //LoadAsync
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,10 @@
 | 
				
			||||||
 | 
					namespace SummerBestWebForm2.AppState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public interface IAppState
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  bool isInit { get; set; }
 | 
				
			||||||
 | 
					  string? myIpAddress { get; set; }
 | 
				
			||||||
 | 
					  Guid SessionId { get; set; }
 | 
				
			||||||
 | 
					  DateTimeOffset DateCreated { get; set; }
 | 
				
			||||||
 | 
					  DateTimeOffset DateExpires { get; set; }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,10 @@
 | 
				
			||||||
 | 
					namespace SummerBestWebForm2.AppState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class MdlAppState : IAppState
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  public bool isInit { get; set; }
 | 
				
			||||||
 | 
					  public string? myIpAddress { get; set; }
 | 
				
			||||||
 | 
					  public Guid SessionId { get; set; }
 | 
				
			||||||
 | 
					  public DateTimeOffset DateCreated { get; set; }
 | 
				
			||||||
 | 
					  public DateTimeOffset DateExpires { get; set; }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,15 @@
 | 
				
			||||||
 | 
					namespace SummerBestWebForm2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class MdlSessionInfo
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  public const string KEY_IPADDRESS = "IPAddress";
 | 
				
			||||||
 | 
					  public string IPAddress { get; set; } = null!;
 | 
				
			||||||
 | 
					  public bool isInit { get; set; } = false;
 | 
				
			||||||
 | 
					  public DateTimeOffset DateCreated { get; set; } = DateTimeOffset.MinValue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public MdlSessionInfo()
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    // Have to have a blank ctor here for ProtectedSessionStorage
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,20 +1,92 @@
 | 
				
			||||||
<!DOCTYPE html>
 | 
					@inject IHttpContextAccessor httpContextAccessor
 | 
				
			||||||
 | 
					<!DOCTYPE html>
 | 
				
			||||||
<html lang="en">
 | 
					<html lang="en">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<head>
 | 
					<head>
 | 
				
			||||||
    <meta charset="utf-8" />
 | 
					  <meta charset="utf-8" />
 | 
				
			||||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 | 
					  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 | 
				
			||||||
    <base href="/" />
 | 
					  <base href="/" />
 | 
				
			||||||
    <link rel="stylesheet" href="bootstrap/bootstrap.min.css" />
 | 
					  <meta name="description" content="KeyMotive's Summer Best Customer Program Enrollment" />
 | 
				
			||||||
    <link rel="stylesheet" href="app.css" />
 | 
					  <meta name="author" content="KeyMotive LLC" />
 | 
				
			||||||
    <link rel="stylesheet" href="SummerBestWebForm2.styles.css" />
 | 
					  <meta name="Charset" content="US-ASCII" />
 | 
				
			||||||
    <link rel="icon" type="image/png" href="favicon.png" />
 | 
					  <meta name="Distribution" content="Global" />
 | 
				
			||||||
    <HeadOutlet @rendermode="InteractiveServer" />
 | 
					  <meta name="Rating" content="General" />
 | 
				
			||||||
 | 
					  <meta name="Robots" content="INDEX,FOLLOW" />
 | 
				
			||||||
 | 
					  <meta name="Revisit-after" content="14 Days" />
 | 
				
			||||||
 | 
					  <link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin />
 | 
				
			||||||
 | 
					  <link href='https://fonts.googleapis.com/css?family=Roboto|Handlee|Candal|Exo 2|Armata|Poppins|Kalam:wght@300&display=swap' rel='stylesheet' type='text/css' />
 | 
				
			||||||
 | 
					  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css" integrity="sha512-jnSuA4Ss2PkkikSOLtYs8BlYIeeIK1h99ty4YfvRPAlzr377vr3CXDb7sb7eEEBYjDtcYj+AjBH3FLv5uSJuXg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
 | 
				
			||||||
 | 
					  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css" integrity="sha512-SnH5WK+bZxgPHs44uWIX+LLJAJ9/2PkPKZ5QiAj6Ta86w+fsb2TkcmfRyVX3pBnMFcV7oQPJkl9QevSCWr3W6A==" crossorigin="anonymous" referrerpolicy="no-referrer" />
 | 
				
			||||||
 | 
					  <link rel="stylesheet" href="app.css" />
 | 
				
			||||||
 | 
					  <link rel="stylesheet" href="SummerBestWebForm2.styles.css" />
 | 
				
			||||||
 | 
					  <link rel="icon" type="image/png" sizes="512x512" href="/img/favico-512.png">
 | 
				
			||||||
 | 
					  <link rel="icon" type="image/png" sizes="256x256" href="/img/favico-256.png">
 | 
				
			||||||
 | 
					  <link rel="icon" type="image/png" sizes="128x128" href="/img/favico-128.png">
 | 
				
			||||||
 | 
					  <link rel="icon" type="image/png" sizes="64x64" href="/img/favico-64.png">
 | 
				
			||||||
 | 
					  <link rel="icon" type="image/png" sizes="32x32" href="/img/favico-32.png">
 | 
				
			||||||
 | 
					  <style type="text/css">
 | 
				
			||||||
 | 
					    div.container {
 | 
				
			||||||
 | 
					      background-color: #fff;
 | 
				
			||||||
 | 
					      min-height: 100vh;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  </style>
 | 
				
			||||||
 | 
					  <HeadOutlet @rendermode="InteractiveServer" />
 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<body>
 | 
					<body>
 | 
				
			||||||
    <Routes @rendermode="InteractiveServer" />
 | 
					  <Routes @rendermode="InteractiveServer" />
 | 
				
			||||||
    <script src="_framework/blazor.web.js"></script>
 | 
					  <script src="_framework/blazor.web.js"></script>
 | 
				
			||||||
 | 
					  <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/js/bootstrap.bundle.min.js" integrity="sha512-7Pi/otdlbbCR+LnW+F7PwFcSDJOuUJB3OxtEHbg4vSMvzvJjde4Po1v4BR9Gdc9aXNUNFVUY+SK51wWT8WF0Gg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
 | 
				
			||||||
 | 
					  <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/js/all.min.js" integrity="sha512-u3fPA7V8qQmhBPNT5quvaXVa1mnnLSXUep5PS1qo5NRzHwG19aHmNJnj1Q8hpA/nBWZtZD4r4AX6YOt5ynLN2g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
 | 
				
			||||||
 | 
					  <script>
 | 
				
			||||||
 | 
					    window.WriteCookie = {
 | 
				
			||||||
 | 
					      WriteCookie: function (name, value, days) {
 | 
				
			||||||
 | 
					        var expires;
 | 
				
			||||||
 | 
					        if (days) {
 | 
				
			||||||
 | 
					          var date = new Date();
 | 
				
			||||||
 | 
					          date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
 | 
				
			||||||
 | 
					          expires = "; expires=" + date.toGMTString();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					          expires = "";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        document.cookie = name + "=" + value + expires + "; path=/";
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    window.ReadCookie = {
 | 
				
			||||||
 | 
					      ReadCookie: function (cname) {
 | 
				
			||||||
 | 
					        var name = cname + "=";
 | 
				
			||||||
 | 
					        var decodedCookie = decodeURIComponent(document.cookie);
 | 
				
			||||||
 | 
					        var ca = decodedCookie.split(';');
 | 
				
			||||||
 | 
					        for (var i = 0; i < ca.length; i++) {
 | 
				
			||||||
 | 
					          var c = ca[i];
 | 
				
			||||||
 | 
					          while (c.charAt(0) == ' ') {
 | 
				
			||||||
 | 
					            c = c.substring(1);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          if (c.indexOf(name) == 0) {
 | 
				
			||||||
 | 
					            return c.substring(name.length, c.length);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return "";
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  </script>
 | 
				
			||||||
 | 
					<pre>@errMsg</pre>
 | 
				
			||||||
 | 
					@if (appState != null)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  <pre style="font-size:.8em">
 | 
				
			||||||
 | 
					    SessionId: @appState.SessionId
 | 
				
			||||||
 | 
					    DateCreated: @appState.DateCreated
 | 
				
			||||||
 | 
					    DateExpires: @appState.DateExpires
 | 
				
			||||||
 | 
					    IPAddress: @appState.myIpAddress
 | 
				
			||||||
 | 
					  </pre>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
</body>
 | 
					</body>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</html>
 | 
					</html>
 | 
				
			||||||
 | 
					@code {
 | 
				
			||||||
 | 
					  [CascadingParameter]
 | 
				
			||||||
 | 
					  public CascadingAppState appState { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private string errMsg = string.Empty;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,3 +8,4 @@
 | 
				
			||||||
@using Microsoft.JSInterop
 | 
					@using Microsoft.JSInterop
 | 
				
			||||||
@using SummerBestWebForm2
 | 
					@using SummerBestWebForm2
 | 
				
			||||||
@using SummerBestWebForm2.Components
 | 
					@using SummerBestWebForm2.Components
 | 
				
			||||||
 | 
					@using SummerBestWebForm2.AppState
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										73
									
								
								Program.cs
								
								
								
								
							
							
						
						| 
						 | 
					@ -1,36 +1,65 @@
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.ResponseCompression;
 | 
				
			||||||
using SummerBestWebForm2.Components;
 | 
					using SummerBestWebForm2.Components;
 | 
				
			||||||
 | 
					using System.IO.Compression;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace SummerBestWebForm2
 | 
					namespace SummerBestWebForm2
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class Program
 | 
					  public class Program
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    public static void Main(string[] args)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public static void Main(string[] args)
 | 
					      var builder = WebApplication.CreateBuilder(args);
 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            var builder = WebApplication.CreateBuilder(args);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Add services to the container.
 | 
					      // Add services to the container.
 | 
				
			||||||
            builder.Services.AddRazorComponents()
 | 
					      builder.Services.AddRazorComponents()
 | 
				
			||||||
                .AddInteractiveServerComponents();
 | 
					          .AddInteractiveServerComponents();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var app = builder.Build();
 | 
					      builder.Services.AddResponseCompression(options =>
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        options.EnableForHttps = true;
 | 
				
			||||||
 | 
					        options.Providers.Add<BrotliCompressionProvider>();
 | 
				
			||||||
 | 
					        options.Providers.Add<GzipCompressionProvider>();
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      builder.Services.Configure<BrotliCompressionProviderOptions>(options =>
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        options.Level = CompressionLevel.Fastest;
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      builder.Services.Configure<GzipCompressionProviderOptions>(options =>
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        options.Level = CompressionLevel.SmallestSize;
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      builder.Services.AddDataProtection();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Configure the HTTP request pipeline.
 | 
					      builder.Services.AddHttpContextAccessor();
 | 
				
			||||||
            if (!app.Environment.IsDevelopment())
 | 
					      builder.Services.AddScoped<HttpContextAccessor>();
 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                app.UseExceptionHandler("/Error");
 | 
					 | 
				
			||||||
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
 | 
					 | 
				
			||||||
                app.UseHsts();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            app.UseHttpsRedirection();
 | 
					      var app = builder.Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            app.UseStaticFiles();
 | 
					      // Configure the HTTP request pipeline.
 | 
				
			||||||
            app.UseAntiforgery();
 | 
					      if (!app.Environment.IsDevelopment())
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        app.UseExceptionHandler("/Error");
 | 
				
			||||||
 | 
					        // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
 | 
				
			||||||
 | 
					        app.UseHsts();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            app.MapRazorComponents<App>()
 | 
					      var webSocketOptions = new WebSocketOptions()
 | 
				
			||||||
                .AddInteractiveServerRenderMode();
 | 
					      {
 | 
				
			||||||
 | 
					        KeepAliveInterval = TimeSpan.FromSeconds(15)
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					      app.UseWebSockets(webSocketOptions);
 | 
				
			||||||
 | 
					      app.UseResponseCompression();
 | 
				
			||||||
 | 
					      app.UseResponseCaching();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            app.Run();
 | 
					      app.UseHttpsRedirection();
 | 
				
			||||||
        }
 | 
					
 | 
				
			||||||
 | 
					      app.UseStaticFiles();
 | 
				
			||||||
 | 
					      app.UseAntiforgery();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      app.MapRazorComponents<App>()
 | 
				
			||||||
 | 
					          .AddInteractiveServerRenderMode();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      app.Run();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,12 @@
 | 
				
			||||||
 | 
					namespace SummerBestWebForm2.SessionState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class MdlSession : Dictionary<string, string>
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  /// <summary>
 | 
				
			||||||
 | 
					  /// Gets or sets the Session Id value.
 | 
				
			||||||
 | 
					  /// </summary>
 | 
				
			||||||
 | 
					  public string SessionId { get; set; } = string.Empty;
 | 
				
			||||||
 | 
					  public string IPAddress { get; set; } = string.Empty;
 | 
				
			||||||
 | 
					  public bool IsCheckedOut { get; set; } = false;
 | 
				
			||||||
 | 
					  public DateTimeOffset dtExpires { get; set; } = DateTimeOffset.Now.AddHours(36);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,57 @@
 | 
				
			||||||
 | 
					namespace SummerBestWebForm2.SessionState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// INTERFACE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public interface ISessionIdManager
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  Task<string?> GetSessionIdAsync();
 | 
				
			||||||
 | 
					  Task<string?> GetIPAddressAsync();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CODE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class SessionIdManager(IHttpContextAccessor httpContextAccessor) : ISessionIdManager
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  private readonly IHttpContextAccessor HttpContextAccessor = httpContextAccessor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public Task<string?> GetSessionIdAsync()
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    var httpContext = HttpContextAccessor.HttpContext;
 | 
				
			||||||
 | 
					    string? result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (httpContext != null)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if (httpContext.Request.Cookies.ContainsKey("sessionId"))
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        result = httpContext.Request.Cookies["sessionId"];
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        result = Guid.NewGuid().ToString();
 | 
				
			||||||
 | 
					        httpContext.Response.Cookies.Append("sessionId", result);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      throw new InvalidOperationException("No HttpContext available");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return Task.FromResult(result);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public Task<string?> GetIPAddressAsync()
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    var httpContext = HttpContextAccessor.HttpContext;
 | 
				
			||||||
 | 
					    string? result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (httpContext != null)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      result = httpContext.Connection.RemoteIpAddress?.ToString();// ?? "Not Set";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      throw new InvalidOperationException("No HttpContext available");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return Task.FromResult(result);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,132 @@
 | 
				
			||||||
 | 
					using System.Net;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace SummerBestWebForm2.SessionState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// INTERFACE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public interface ISessionManager
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  Task<MdlSession> GetSession();
 | 
				
			||||||
 | 
					  Task UpdateSession(MdlSession session);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CODE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// <summary>
 | 
				
			||||||
 | 
					/// Dictionary containing per-user session objects, keyed
 | 
				
			||||||
 | 
					/// by sessionId.
 | 
				
			||||||
 | 
					/// </summary>
 | 
				
			||||||
 | 
					public class SessionManager : ISessionManager
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  private Dictionary<Guid, MdlSession> _sessions = new Dictionary<Guid, MdlSession>();
 | 
				
			||||||
 | 
					  private readonly ISessionIdManager _sessionIdManager;
 | 
				
			||||||
 | 
					  private object syncLock1 = new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public SessionManager(ISessionIdManager sessionIdManager)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    _sessionIdManager = sessionIdManager;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public async Task<MdlSession> GetSession()
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    string key = await _sessionIdManager.GetSessionIdAsync() ?? "";
 | 
				
			||||||
 | 
					    string ipAddress = await _sessionIdManager.GetIPAddressAsync() ?? "";
 | 
				
			||||||
 | 
					    Guid theSessionId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!Guid.TryParse(key, out theSessionId))
 | 
				
			||||||
 | 
					      theSessionId = Guid.Empty;//Guid.Parse(key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    MdlSession session;
 | 
				
			||||||
 | 
					    bool needToCreateNew = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!_sessions.ContainsKey(theSessionId))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      needToCreateNew = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else if (_sessions[theSessionId].dtExpires < DateTimeOffset.Now)
 | 
				
			||||||
 | 
					      needToCreateNew = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (needToCreateNew)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      session = new MdlSession() { SessionId = key, IPAddress = ipAddress };
 | 
				
			||||||
 | 
					      _sessions.Add(theSessionId, new MdlSession());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					      session = _sessions[theSessionId];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ensure session isn't checked out by wasm
 | 
				
			||||||
 | 
					    //while (session.IsCheckedOut)
 | 
				
			||||||
 | 
					    //    await Task.Delay(5);
 | 
				
			||||||
 | 
					    var endTime = DateTime.UtcNow.AddSeconds(10);
 | 
				
			||||||
 | 
					    while (session.IsCheckedOut)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if (DateTime.UtcNow > endTime)
 | 
				
			||||||
 | 
					        throw new TimeoutException();
 | 
				
			||||||
 | 
					      await Task.Delay(5);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return session;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public async Task UpdateSession(MdlSession session)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    if (session != null)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      string key = await _sessionIdManager.GetSessionIdAsync() ?? "";
 | 
				
			||||||
 | 
					      string ipAddress = await _sessionIdManager.GetIPAddressAsync() ?? "";
 | 
				
			||||||
 | 
					      Guid theSessionId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (!Guid.TryParse(key, out theSessionId))
 | 
				
			||||||
 | 
					        theSessionId = Guid.Empty;//Guid.Parse(key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (_sessions.ContainsKey(theSessionId))
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        //session = new MdlSession() { SessionId = key, IPAddress = ipAddress };
 | 
				
			||||||
 | 
					        session.SessionId = key;
 | 
				
			||||||
 | 
					        session.dtExpires = DateTimeOffset.Now.AddHours(36);  // Rolling expiration date
 | 
				
			||||||
 | 
					        _sessions[theSessionId] = session;
 | 
				
			||||||
 | 
					        _sessions[theSessionId].IsCheckedOut = false;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      else
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        var sess = new MdlSession() { SessionId = key, IPAddress = ipAddress };
 | 
				
			||||||
 | 
					        _sessions[theSessionId] = session;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Remove stale sessions
 | 
				
			||||||
 | 
					      lock (syncLock1)
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        try
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          var lstRemove = new HashSet<Guid>();
 | 
				
			||||||
 | 
					          foreach (var kvp in _sessions)
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            if (kvp.Value.dtExpires < DateTimeOffset.Now && !lstRemove.Contains(kvp.Key))
 | 
				
			||||||
 | 
					              lstRemove.Add(kvp.Key);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          foreach (var rmv in lstRemove)
 | 
				
			||||||
 | 
					            _sessions.Remove(rmv);
 | 
				
			||||||
 | 
					          lstRemove.Clear();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (Exception ex)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          // do something
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  } //UpdateSession
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// <summary>
 | 
				
			||||||
 | 
					  /// Replace the contents of oldSession with the items
 | 
				
			||||||
 | 
					  /// in newSession.
 | 
				
			||||||
 | 
					  /// </summary>
 | 
				
			||||||
 | 
					  /// <param name="newSession"></param>
 | 
				
			||||||
 | 
					  /// <param name="oldSession"></param>
 | 
				
			||||||
 | 
					  private void Replace(MdlSession newSession, MdlSession oldSession)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    oldSession.Clear();
 | 
				
			||||||
 | 
					    foreach (var key in newSession.Keys)
 | 
				
			||||||
 | 
					      oldSession.Add(key, newSession[key]);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@ using System.Reflection;
 | 
				
			||||||
[assembly: System.Reflection.AssemblyCompanyAttribute("SummerBestWebForm2")]
 | 
					[assembly: System.Reflection.AssemblyCompanyAttribute("SummerBestWebForm2")]
 | 
				
			||||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
 | 
					[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
 | 
				
			||||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
 | 
					[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
 | 
				
			||||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
 | 
					[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+92bc4657300f745b2cfa9a7b2055bd3fd4591564")]
 | 
				
			||||||
[assembly: System.Reflection.AssemblyProductAttribute("SummerBestWebForm2")]
 | 
					[assembly: System.Reflection.AssemblyProductAttribute("SummerBestWebForm2")]
 | 
				
			||||||
[assembly: System.Reflection.AssemblyTitleAttribute("SummerBestWebForm2")]
 | 
					[assembly: System.Reflection.AssemblyTitleAttribute("SummerBestWebForm2")]
 | 
				
			||||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
 | 
					[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1 +1 @@
 | 
				
			||||||
98a44021c4ce1eb513aa177c5c56797f58eab237be1d5eb9823389d6970b0f70
 | 
					17e471ad6ef47e2a4bd7241cad398f501e58c3d66500f6f4af29608363c718b3
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,6 +20,10 @@ build_property._RazorSourceGeneratorDebug =
 | 
				
			||||||
build_property.EffectiveAnalysisLevelStyle = 8.0
 | 
					build_property.EffectiveAnalysisLevelStyle = 8.0
 | 
				
			||||||
build_property.EnableCodeStyleSeverity = 
 | 
					build_property.EnableCodeStyleSeverity = 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[C:/Repos/SummerBestWebForm2/AppState/CascadingAppState.razor]
 | 
				
			||||||
 | 
					build_metadata.AdditionalFiles.TargetPath = QXBwU3RhdGVcQ2FzY2FkaW5nQXBwU3RhdGUucmF6b3I=
 | 
				
			||||||
 | 
					build_metadata.AdditionalFiles.CssScope = 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[C:/Repos/SummerBestWebForm2/Components/App.razor]
 | 
					[C:/Repos/SummerBestWebForm2/Components/App.razor]
 | 
				
			||||||
build_metadata.AdditionalFiles.TargetPath = Q29tcG9uZW50c1xBcHAucmF6b3I=
 | 
					build_metadata.AdditionalFiles.TargetPath = Q29tcG9uZW50c1xBcHAucmF6b3I=
 | 
				
			||||||
build_metadata.AdditionalFiles.CssScope = 
 | 
					build_metadata.AdditionalFiles.CssScope = 
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 31 KiB  | 
| 
		 After Width: | Height: | Size: 41 KiB  | 
| 
		 After Width: | Height: | Size: 6.0 KiB  | 
| 
		 After Width: | Height: | Size: 13 KiB  | 
| 
		 After Width: | Height: | Size: 1.2 KiB  | 
| 
		 After Width: | Height: | Size: 17 KiB  | 
| 
		 After Width: | Height: | Size: 2.7 KiB  | 
| 
		 After Width: | Height: | Size: 50 KiB  |