|
Stop. Click here for the updated and (likely final) version of this stuff!
This page is kept for archive purposes only.
If you like what this code does for you, you can keep it. The comments should be good enough, so I just post this as-is instead of a lengthy blurb. (And someone please tell me how I can tell Radio not to render all comments as links; I am too lazy to figure that out myself :-S). I can just copy and paste into notepad fine from the page, though, so you should be able to as well.
Documentation update: I made an error in my own example ;) The state members must be public or protected.
Here's a hint:
public class MyPage : StateManagingPage { [PersistentPageState] public int pageVisitsEver; [SessionPageState] public int pageVisitsThisSession; [TransientPageState] public int roundtripsThisPage; /* omissions... */ private void Page_Load(object sender, System.EventArgs e) { pageVisitsEver++; pageVisitsThisSession++; roundtripsThisPage++; } }
Enjoy!
// Copyright (c) 2002, newtelligence AG. (http://www.newtelligence.com) // All rights reserved. // Author: Clemens F. Vasters (clemensv@newtelligence.com)
using System; using System.Reflection; using System.Web; using System.Web.UI; using System.Runtime.Serialization; using System.Globalization;
namespace newtelligence.Web.UI { /// <summary> /// Use this attribute to mark all protected or public /// fields that your want to auto-serialize into /// session state and which are scoped to the current /// conversation only. /// </summary> [AttributeUsage(AttributeTargets.Field)] public class TransientPageStateAttribute : Attribute { public TransientPageStateAttribute() { } }
/// <summary> /// Use this attribute to mark all protected or public /// fields that your want to auto-serialize into /// session state and which are scoped to the session. /// </summary> [AttributeUsage(AttributeTargets.Field)] public class SessionPageStateAttribute : Attribute { public SessionPageStateAttribute() { } }
/// <summary> /// Use this attribute to mark all protected or public /// fields that your want to auto-serialize into /// a permanent cookie and which are scope across /// sessions. /// </summary> [AttributeUsage(AttributeTargets.Field)] public class PersistentPageStateAttribute : Attribute { public PersistentPageStateAttribute() { } }
/// <summary> /// This is a base class for your own Pages which implements /// automatic serialization of public and protected fields /// into session state or into cookies. /// </summary> /// <example> /// All you need to do is this: /// /// using newtelligence.Web.UI /// /// public class MyPage : StateManagingPage /// { /// [PersistentPageState] int pageVisitsEver; /// [SessionPageState] int pageVisitsThisSession; /// [TransientPageState] int roundtripsThisPage; /// /// // omissions... /// /// private void Page_Load(object sender, System.EventArgs e) /// { /// pageVisitsEver++; /// pageVisitsThisSession++; /// roundtripsThisPage++; /// } /// /// } /// </example> /// <remarks> /// This should really be: /// /// template <class T> /// public class StateManagingPage : T /// /// but we don't have generics, yet. Sigh. /// </remarks> public abstract class StateManagingPage : System.Web.UI.Page { private EventHandler PreRenderHandler = null;
/// <summary> /// Public constructor. Sets up events. /// </summary> public StateManagingPage() { Init += new EventHandler(this.SessionLoad); PreRender += PreRenderHandler = new EventHandler(this.SessionStore); }
/// <summary> /// Event handler attached to "Page.Init" that recovers /// marked fields from session state /// </summary> /// <param name="o">Object firing the event</param> /// <param name="e">Event arguments</param> private void SessionLoad(object o, EventArgs e) {
FieldInfo[] fields = GetType().GetFields(BindingFlags.Public| BindingFlags.NonPublic| BindingFlags.Instance);
// Persistent values HttpCookie pageCookie = Request.Cookies[GetType().FullName]; if ( pageCookie != null ) { foreach( FieldInfo field in fields ) { if ( field.IsDefined(typeof(PersistentPageStateAttribute), true ) && field.FieldType.IsPrimitive ) { field.SetValue(this, Convert.ChangeType(pageCookie[field.Name], field.FieldType, CultureInfo.InvariantCulture)); } } }
// Session scope values foreach( FieldInfo field in fields ) { if ( field.IsDefined(typeof(SessionPageStateAttribute),true ) && field.FieldType.IsSerializable ) { field.SetValue(this, Session[field.DeclaringType.FullName+"."+field.Name]); } }
if ( IsPostBack ) { // Conversation scope values foreach( FieldInfo field in fields) { if ( field.IsDefined(typeof(TransientPageStateAttribute), true ) && field.FieldType.IsSerializable ) { field.SetValue(this, Session[field.DeclaringType.FullName+"."+field.Name]); } } } }
/// <summary> /// Event handler attached to "Page.Unload" that sticks marked fields /// into session state /// </summary> /// <param name="o">Object firing the event</param> /// <param name="e">Event arguments</param> private void SessionStore(object o, EventArgs e) {
FieldInfo[] fields = GetType().GetFields(BindingFlags.Public| BindingFlags.NonPublic| BindingFlags.Instance);
// Persistent state HttpCookie pageCookie = new HttpCookie(GetType().FullName); pageCookie.Expires = DateTime.Now.AddYears(2);
foreach( FieldInfo field in fields ) { if ( field.IsDefined(typeof(PersistentPageStateAttribute),true )) { if ( field.FieldType.IsPrimitive ) { pageCookie[field.Name] = (string)Convert.ChangeType(field.GetValue(this), typeof(string), CultureInfo.InvariantCulture); } else { throw new SerializationException( String.Format("Field \"{0}" is not a primitive type",field.Name)); } } } Response.AppendCookie(pageCookie);
// Transient & Session scope state foreach( FieldInfo field in fields) { if ( field.IsDefined(typeof(TransientPageStateAttribute),true ) || field.IsDefined(typeof(SessionPageStateAttribute),true ) ) { if ( field.FieldType.IsSerializable ) { Session[field.DeclaringType.FullName+"."+field.Name] = field.GetValue(this); } else { throw new SerializationException( String.Format("Type {0} of field \"{0}" is not serializable", field.FieldType.FullName,field.Name)); } } } }
/// <summary> /// Private utility function invoked by Transfer and Redirect. /// Clears out all transient state of the current page. /// </summary> private void SessionDiscard() { // Discard transient page state only foreach( FieldInfo field in GetType().GetFields(BindingFlags.Public| BindingFlags.NonPublic| BindingFlags.Instance)) { if ( field.IsDefined(typeof(TransientPageStateAttribute),true ) && field.FieldType.IsSerializable ) { Session.Remove(field.DeclaringType.FullName+"."+field.Name); } } }
/// <summary> /// Use this as a replacement for Server.Transfer. This /// method will discard the transient page state and /// call Server.Transfer. /// </summary> /// <param name="target"></param> public virtual void Transfer( string target ) { SessionDiscard(); Unload -= PreRenderHandler; Server.Transfer(target); }
/// <summary> /// Use this as a replacement for Response.Redirect. This /// method will discard the transient page state and /// call Response.Redirect. /// </summary> /// <param name="target"></param> public virtual void Redirect( string target ) { SessionDiscard(); Unload -= PreRenderHandler; Response.Redirect(target); } } }
// Copyright (c) 2002, newtelligence AG. (http://www.newtelligence.com) // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are permitted provided // that the following conditions are met: // // (1) Redistributions of source code must retain the above copyright notice, this list // of conditions and the following disclaimer. // (2) Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // (3) Neither the name of the newtelligence AG nor the names of its contributors may be used // to endorse or promote products derived from this software without specific prior // written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS // OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT // OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|