Added SessionState Components
| 
						 | 
				
			
			@ -1,7 +1,48 @@
 | 
			
		|||
{
 | 
			
		||||
  "Version": 1,
 | 
			
		||||
  "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": [
 | 
			
		||||
    {
 | 
			
		||||
      "Orientation": 0,
 | 
			
		||||
| 
						 | 
				
			
			@ -9,7 +50,7 @@
 | 
			
		|||
      "DocumentGroups": [
 | 
			
		||||
        {
 | 
			
		||||
          "DockedWidth": 200,
 | 
			
		||||
          "SelectedChildIndex": -1,
 | 
			
		||||
          "SelectedChildIndex": 11,
 | 
			
		||||
          "Children": [
 | 
			
		||||
            {
 | 
			
		||||
              "$type": "Bookmark",
 | 
			
		||||
| 
						 | 
				
			
			@ -54,6 +95,136 @@
 | 
			
		|||
            {
 | 
			
		||||
              "$type": "Bookmark",
 | 
			
		||||
              "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">
 | 
			
		||||
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="utf-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 | 
			
		||||
    <base href="/" />
 | 
			
		||||
    <link rel="stylesheet" href="bootstrap/bootstrap.min.css" />
 | 
			
		||||
    <link rel="stylesheet" href="app.css" />
 | 
			
		||||
    <link rel="stylesheet" href="SummerBestWebForm2.styles.css" />
 | 
			
		||||
    <link rel="icon" type="image/png" href="favicon.png" />
 | 
			
		||||
    <HeadOutlet @rendermode="InteractiveServer" />
 | 
			
		||||
  <meta charset="utf-8" />
 | 
			
		||||
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 | 
			
		||||
  <base href="/" />
 | 
			
		||||
  <meta name="description" content="KeyMotive's Summer Best Customer Program Enrollment" />
 | 
			
		||||
  <meta name="author" content="KeyMotive LLC" />
 | 
			
		||||
  <meta name="Charset" content="US-ASCII" />
 | 
			
		||||
  <meta name="Distribution" content="Global" />
 | 
			
		||||
  <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>
 | 
			
		||||
 | 
			
		||||
<body>
 | 
			
		||||
    <Routes @rendermode="InteractiveServer" />
 | 
			
		||||
    <script src="_framework/blazor.web.js"></script>
 | 
			
		||||
  <Routes @rendermode="InteractiveServer" />
 | 
			
		||||
  <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>
 | 
			
		||||
 | 
			
		||||
</html>
 | 
			
		||||
@code {
 | 
			
		||||
  [CascadingParameter]
 | 
			
		||||
  public CascadingAppState appState { get; set; }
 | 
			
		||||
 | 
			
		||||
  private string errMsg = string.Empty;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,3 +8,4 @@
 | 
			
		|||
@using Microsoft.JSInterop
 | 
			
		||||
@using SummerBestWebForm2
 | 
			
		||||
@using SummerBestWebForm2.Components
 | 
			
		||||
@using SummerBestWebForm2.AppState
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										73
									
								
								Program.cs
								
								
								
								
							
							
						
						| 
						 | 
				
			
			@ -1,36 +1,65 @@
 | 
			
		|||
using Microsoft.AspNetCore.ResponseCompression;
 | 
			
		||||
using SummerBestWebForm2.Components;
 | 
			
		||||
using System.IO.Compression;
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
            builder.Services.AddRazorComponents()
 | 
			
		||||
                .AddInteractiveServerComponents();
 | 
			
		||||
      // Add services to the container.
 | 
			
		||||
      builder.Services.AddRazorComponents()
 | 
			
		||||
          .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.
 | 
			
		||||
            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();
 | 
			
		||||
            }
 | 
			
		||||
      builder.Services.AddHttpContextAccessor();
 | 
			
		||||
      builder.Services.AddScoped<HttpContextAccessor>();
 | 
			
		||||
 | 
			
		||||
            app.UseHttpsRedirection();
 | 
			
		||||
      var app = builder.Build();
 | 
			
		||||
 | 
			
		||||
            app.UseStaticFiles();
 | 
			
		||||
            app.UseAntiforgery();
 | 
			
		||||
      // Configure the HTTP request pipeline.
 | 
			
		||||
      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>()
 | 
			
		||||
                .AddInteractiveServerRenderMode();
 | 
			
		||||
      var webSocketOptions = new WebSocketOptions()
 | 
			
		||||
      {
 | 
			
		||||
        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.AssemblyConfigurationAttribute("Debug")]
 | 
			
		||||
[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.AssemblyTitleAttribute("SummerBestWebForm2")]
 | 
			
		||||
[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.EnableCodeStyleSeverity = 
 | 
			
		||||
 | 
			
		||||
[C:/Repos/SummerBestWebForm2/AppState/CascadingAppState.razor]
 | 
			
		||||
build_metadata.AdditionalFiles.TargetPath = QXBwU3RhdGVcQ2FzY2FkaW5nQXBwU3RhdGUucmF6b3I=
 | 
			
		||||
build_metadata.AdditionalFiles.CssScope = 
 | 
			
		||||
 | 
			
		||||
[C:/Repos/SummerBestWebForm2/Components/App.razor]
 | 
			
		||||
build_metadata.AdditionalFiles.TargetPath = Q29tcG9uZW50c1xBcHAucmF6b3I=
 | 
			
		||||
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  |