/// <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); }
/// <summary> /// Custom Controller factory method returning a serialized object if available. /// </summary> /// <param name="actionContext"></param> /// <returns></returns> public object Create(ControllerContext actionContext) { object controller = null; var controllerTypeInfo = actionContext.ActionDescriptor.ControllerTypeInfo; var controllerType = controllerTypeInfo.AsType(); var storageID = StorageImplementation.GetStorageID(controllerTypeInfo.Name); var sessionStorageID = StorageImplementation.GetSessionStorageID(controllerTypeInfo.Name); var storage = StorageImplementation.GetStorage(this.Configuration, this.HttpContext, sessionStorageID); StorageImplementation.ClearIfRequested(this.HttpContext, storage, storageID); Guid sessionOverride; Guid session; byte[] bytes; Func <byte[], byte[]> filter = null; // ---------- Direct GET request ?session= from the Database ---------- if (this.HttpContext.Request.Method == WebRequestMethods.Http.Get && Guid.TryParse(this.HttpContext.Request.Query["session"], out sessionOverride)) { using (var db = new ASP_DBEntities()) { (bytes, filter) = StorageImplementation.DatabaseBytes(Configuration, HttpContext, storageID, sessionOverride); controller = DeserializeController(actionContext, controllerTypeInfo, controllerType, bytes, filter); } } else { // ---------- Load from ViewState ---------- if (storage == Storage.ViewState && this.HttpContext.Request.Method == WebRequestMethods.Http.Post && this.HttpContext.Request.Form.ContainsKey(storageID)) { // input type=hidden from <input viewstate="@ViewBag.ViewState" /> var controllerString = this.HttpContext.Request.Form[storageID]; if (!String.IsNullOrEmpty(controllerString)) { (bytes, filter) = StorageImplementation.ViewStateBytes(this.Configuration, controllerString); controller = DeserializeController(actionContext, controllerTypeInfo, controllerType, bytes, filter); } } // ---------- Load from Session ---------- else if (storage == Storage.Session && this.HttpContext.Session.TryGetValue(storageID, out bytes)) { controller = DeserializeController(actionContext, controllerTypeInfo, controllerType, bytes); } // ---------- Load from Database ---------- else if (storage == Storage.Database && Guid.TryParse(this.HttpContext.Request.Cookies[storageID].FromCookieString()["session"], out session)) { (bytes, filter) = StorageImplementation.DatabaseBytes(Configuration, HttpContext, storageID, session); controller = DeserializeController(actionContext, controllerTypeInfo, controllerType, bytes, filter); ((IPersistentController)controller).Session = session; } // ---------- Load from X-ViewState Header ---------- else if (storage == Storage.Header && this.HttpContext.Request.Headers.ContainsKey(StorageImplementation.HeaderName)) { // input type=hidden from <input viewstate="@ViewBag.ViewState" /> var controllerString = this.HttpContext.Request.Headers[StorageImplementation.HeaderName]; if (!String.IsNullOrEmpty(controllerString)) { (bytes, filter) = StorageImplementation.ViewStateBytes(this.Configuration, controllerString); controller = DeserializeController(actionContext, controllerTypeInfo, controllerType, bytes, filter); } } if (controller == null) { // ASP.NET Core implementation, no persistence, just return the new controller controller = actionContext.HttpContext.RequestServices.GetService(controllerType); } } return(controller); }