|
-----------8<---------cut here ---------8<--------------
// Copyright (c) 2002, newtelligence AG. (http://www.newtelligence.com) // All rights reserved. // Author: Clemens F. Vasters (clemensv@newtelligence.com) // // Revision 1: Added cross-page, cross-session state
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 { /// <summary> /// Enables auto-serialization of this field into /// conversation (page scoped session) state. The /// data type must be serializable. /// </summary> 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 { internal string keyName = null;
/// <summary> /// Enables auto-serialization of this field into /// session state (session scope). The data type /// must be serializable. /// </summary> public SessionPageStateAttribute() { }
/// <summary> /// Enables auto-serialization of this field into /// session state (session scope). The data type /// must be serializable. /// </summary> /// <param name="KeyName">A site-unique key that enables /// sharing of this state element value across pages</param> /// <remarks> /// This variant of this attribute enables declarative /// sharing of session state between pages. Works with /// Redirects and Transfers. /// </remarks> public SessionPageStateAttribute(string KeyName) { keyName = KeyName; } }
/// <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 { internal string keyName = null;
/// <summary> /// Enables auto-serialization of this field into /// a persistent state cookie (user, page scope). /// The data type must be serializable. /// </summary> public PersistentPageStateAttribute() { }
/// <summary> /// Enables auto-serialization of this field into /// a persistent state cookie (user scope). The data type /// must be serializable. /// </summary> /// <param name="KeyName">A site-unique key that enables /// sharing of this state element value across pages /// and sessions</param> /// <remarks> /// This variant of this attribute enables declarative /// sharing of session state between pages and across visits. /// Works with Redirects and Transfers. /// </remarks> public PersistentPageStateAttribute(string KeyName) { keyName = KeyName; } }
/// <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] public int pageVisitsEver; /// [PersistentPageState("Visits")] public int siteVisitsEver; /// [SessionPageState] public int pageVisitsThisSession; /// [SessionPageState("Visits")] public int siteVisitsThisSession; /// [TransientPageState] public int roundtripsThisPage; /// /// // omissions... /// /// private void Page_Load(object sender, System.EventArgs e) /// { /// pageVisitsEver++; /// siteVisitsEver++; /// pageVisitsThisSession++; /// siteVisitsThisSession++; /// roundtripsThisPage++; /// } /// /// } /// </example> /// <remarks> /// This should really be: /// /// template <class T> /// public class StateManagingPage : T /// /// but we don't have generics, yet. Sigh. /// </remarks> public class StateManagingPage : System.Web.UI.Page { private EventHandler PreRenderHandler = null; private const string keyPrefix = "__$stateManagingPages";
/// <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, page scope values HttpCookie pageCookie = Request.Cookies[GetType().FullName]; if ( pageCookie != null ) { foreach( FieldInfo field in fields ) { if ( field.IsDefined(typeof(PersistentPageStateAttribute), true ) && field.FieldType.IsPrimitive ) { PersistentPageStateAttribute ppsa = (PersistentPageStateAttribute) field.GetCustomAttributes( typeof(PersistentPageStateAttribute),true)[0]; if ( ppsa.keyName == null && pageCookie[field.Name] != null ) { field.SetValue(this, Convert.ChangeType(pageCookie[field.Name], field.FieldType, CultureInfo.InvariantCulture)); } } } }
// Persistent, user scope values HttpCookie siteCookie = Request.Cookies[keyPrefix]; if ( siteCookie != null ) { foreach( FieldInfo field in fields ) { if ( field.IsDefined(typeof(PersistentPageStateAttribute), true ) && field.FieldType.IsPrimitive ) { PersistentPageStateAttribute ppsa = (PersistentPageStateAttribute) field.GetCustomAttributes( typeof(PersistentPageStateAttribute),true)[0];
if ( ppsa.keyName != null && siteCookie[ppsa.keyName] != null ) { field.SetValue(this, Convert.ChangeType(siteCookie[ppsa.keyName], field.FieldType, CultureInfo.InvariantCulture)); } } } }
// Session scope values foreach( FieldInfo field in fields ) { if ( field.IsDefined(typeof(SessionPageStateAttribute),true ) && field.FieldType.IsSerializable ) { SessionPageStateAttribute spsa = (SessionPageStateAttribute) field.GetCustomAttributes( typeof(SessionPageStateAttribute),true)[0];
if ( spsa.keyName == null ) { field.SetValue(this, Session[field.DeclaringType.FullName+"."+field.Name]); } else { field.SetValue(this, Session[keyPrefix+spsa.keyName]); } } }
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); HttpCookie siteCookie = Request.Cookies[keyPrefix];
if ( siteCookie == null ) { siteCookie = new HttpCookie(keyPrefix); } siteCookie.Expires = pageCookie.Expires = DateTime.Now.AddYears(2);
foreach( FieldInfo field in fields ) { if ( field.IsDefined(typeof(PersistentPageStateAttribute),true )) { if ( field.FieldType.IsPrimitive ) { PersistentPageStateAttribute ppsa = (PersistentPageStateAttribute) field.GetCustomAttributes( typeof(PersistentPageStateAttribute),true)[0]; if ( ppsa.keyName == null ) { pageCookie[field.Name] = (string)Convert.ChangeType(field.GetValue(this), typeof(string), CultureInfo.InvariantCulture); } else { siteCookie[ppsa.keyName] = (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)); } } }
if ( pageCookie.Values.Count > 0 ) { Response.AppendCookie(pageCookie); } if ( siteCookie.Values.Count > 0 ) { Response.AppendCookie(siteCookie); }
// Transient & Session scope state foreach( FieldInfo field in fields) { if ( field.IsDefined(typeof(TransientPageStateAttribute),true ) || field.IsDefined(typeof(SessionPageStateAttribute),true ) ) { if ( field.FieldType.IsSerializable ) { string fieldName;
fieldName = field.DeclaringType.FullName+"."+field.Name; if ( field.IsDefined(typeof(SessionPageStateAttribute),true) ) { SessionPageStateAttribute spsa = (SessionPageStateAttribute) field.GetCustomAttributes( typeof(SessionPageStateAttribute),true)[0]; if ( spsa.keyName != null ) { fieldName = keyPrefix+spsa.keyName; } }
Session[fieldName] = 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 ) { // store persistent session aspects, since PreRender isn't fired now SessionStore(null, null); // and then discard the transient state SessionDiscard(); 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 ) { // store persistent session aspects, since PreRender isn't fired now SessionStore(null, null); // and then discard the transient state SessionDiscard(); 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.
|