/// <summary> /// Load/Clear each class and session only once. /// </summary> /// <param name="action">Load or Clear</param> /// <param name="storageID">Class identifier (there can be multiple /// persistent classes to load on a page)</param> /// <returns>true when called the first time for that storageID, false afterwards</returns> private static bool Once(OnceAction action, string storageID) { var onceKey = RequestQuerySessionMiddleware.SessionOnceKey(action); HashSet <string> done; bool isDone; if (HttpContext.Session.TryGetValue(onceKey, out byte[] bytes))
/// <summary> /// Mirrors the MVC PersistentControllerActivator for the WebSharper /// context. /// </summary> /// <typeparam name="M"></typeparam> /// <param name="viewModelAccessor">non-thread safe static reference to the ViewModel</param> /// <returns></returns> // [Remote] -> Yields "Remote methods must not be generic" from WebSharper: // https://github.com/dotnet-websharper/core/issues/1048 public static M Load <M, V>(string viewState, out V viewModelAccessor, Storage?sessionStorage = null) where M : class, IStored <M>, new() where V : ViewModel <M>, new() { var storage = sessionStorage ?? StorageImplementation.GetStorage(Configuration, HttpContext); var storageID = StorageImplementation.GetStorageID(typeof(M).Name); var getQuery = RequestQuerySessionMiddleware.Query(HttpContext); // Implements StorageImplementation.ClearIfRequested(HttpContext, storage, storageID) // for WebSharper Ajax POST requests if (getQuery != null && getQuery.TryGetValue("clear", out string getClear) && bool.TryParse(getClear, out bool _) && Once(OnceAction.Clear, storageID)) { getQuery.TryGetValue("storage", out string getStorage); Enum.TryParse <Storage>(getStorage, true, out Storage clearStorage); StorageImplementation.Clear(HttpContext, clearStorage, storage, storageID); } V viewModel; Guid sessionOverride; Guid session; byte[] bytes; Func <byte[], byte[]> filter; if (getQuery != null && getQuery.TryGetValue("session", out string getSession) && Guid.TryParse(getSession, out sessionOverride) && Once(OnceAction.Load, storageID)) { // ---------- Direct Load Database ---------- viewModel = new V(); (bytes, filter) = StorageImplementation.DatabaseBytes(Configuration, HttpContext, storageID, sessionOverride); viewModel.Main = (M)StorageImplementation.LoadFromBytes(() => new M(), bytes, filter); } else { // ---------- Load ViewState ---------- if (storage == Storage.ViewState) { viewModel = new V(); filter = StorageImplementation.DecryptViewState(Configuration); viewModel.ViewState = viewState; viewModel.DeserializeMain(filter); } // ---------- Load Session ---------- else if (storage == Storage.Session) { HttpContext.Session.TryGetValue(storageID, out bytes); viewModel = new V(); viewModel.SetMain(StorageImplementation.LoadFromBytes(() => new M(), bytes)); } // ---------- Load Database ---------- else if (storage == Storage.Database) { Guid.TryParse(HttpContext.Request.Cookies[storageID].FromCookieString()["session"], out session); (bytes, filter) = StorageImplementation.DatabaseBytes(Configuration, HttpContext, storageID, session); viewModel = new V(); viewModel.SetMain(StorageImplementation.LoadFromBytes(() => new M(), bytes, filter)); } else { throw new NotImplementedException("Storage {0} not implemented"); } } // Now that an instance is guaranteed remember the storage type for Save() and the client. viewModel.SessionStorage = storage; viewModel.VSessionStorage = storage.ToString(); // An instantiated Main is now guaranteed -> make its members // visible to WebSharper: viewModel.LoadMembers(); // Include the ViewModel instance as member of the returned Main viewModel.Main.ViewModel = viewModel; // Set a reference to the ViewModel viewModelAccessor = viewModel; return(viewModel.Main); }