//  home   //  advanced search   //  news   //  categories   //  sql build chart   //  downloads   //  statistics
 ASP FAQ 
Home
ASP FAQ Tutorials

   8000XXXX Errors
   Alerts
   ASP.NET 2.0
      .NET Provider Pattern
      Basic Language Constructs
      Themes & Skins (web)
      Tips & Techniques
      XML Serialization
   Classic ASP 1.0
   Databases
   General Concepts
   Search Engine Optimization (SEO)

Contact Us
Site Map

Search

Web
aspfaq.com
tutorials.aspfaq.com
asp.net2.aspfaq.com

ASP FAQ Tutorials :: ASP.NET 2.0 :: .NET Provider Pattern :: Provider Pattern Part 2: Implementing the first 2 providers


Provider Pattern Part 2: Implementing the first 2 providers

If you have not already done so, you should take a quick look at Part 1 - which describes what we're trying to do here.

Now that we know what  we want to do, the question is how to do it?  To being with, we will implement the Session and Application providers which will let us use this new API to store things to the Session and Application caches provided by .NET.

Before we get to that though, we need to look at the StatePath class which is responsible for handling all functionality related to paths.  Everything is indexed/referenced by a StatePath which can be displayed in text as //ProviderName/Path1/Path2/Etc

StatePath class

The statePath class keeps track of the provider for a path, as well as the "LocalPath" which is anything following the provider statement.  So the local path of //Session/Users/Joe/Preferences woud be /Users/Joe/Preferences.

CSharp



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UBR.Products.State.Lib.Provider;
using UBR.Products.State.Lib.Interfaces;
namespace UBR.Products.State.Lib.Provider
{
    public class StatePath
    {
        public StatePath() { }
        public StatePath(string path)
        {
            FromString(path);
        } 

        public StatePath(StatePath path)
        {
            this.Provider = path.Provider;
            foreach (string part in path.PathParts) this.PathParts.Add(part);
        }

        public void FromString(string path)
        {
            // Strip off the 1st part
            if (!path.StartsWith("//")) throw new Exception("Path must start with //");
            this.PathParts = new List<string>(path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries));
            // If there's at least 1 node, then set the provider and remove that node...
            if (this.PathParts.Count > 0)
            {
                this.Provider = SPM.Providers[this.PathParts[0]];
                this.PathParts.RemoveAt(0);
            }
        }

        public string LocalPath
        {
            get
            {
                StringBuilder sb = new StringBuilder();
                foreach (string part in this.PathParts) sb.AppendFormat("/{0}", part);
                return sb.ToString();
            }
        }

        public bool RootNode
        {
            get { return this.PathParts.Count == 0; }
        }

        public string Name
        {
            get
            {
                if (this.RootNode) return this.Provider.Name;
                else return this.PathParts[this.PathParts.Count - 1];
            }
            set
            {
                throw new Exception("Name can not be altered at this time...");
            }
        }

        public StatePath ParentPath
        {
            get
            {
                if (this.RootNode) return null;
                else
                {
                    StatePath parentPath = new StatePath(this);
                    parentPath.PathParts.RemoveAt(parentPath.PathParts.Count - 1);
                    return parentPath;
                }
            }
        }

        public StatePath(IStateNode node)
        {
            FromStateNode(node);
        }

        private void FromStateNode(IStateNode node)
        {
            if (node.Parent == null)
            {
                this.Provider = SPM.Providers[node.Name];
            }
            else
            {
                this.Provider = node.Parent.Path.Provider;
                foreach (string part in node.Parent.Path.PathParts) this.PathParts.Add(part);
                this.PathParts.Add(node.Name);
            }
        }

        private StateProviderBase m_provider = SPM.Provider;
        public StateProviderBase Provider
        {
            get { return m_provider; }
            set { m_provider = value; }
        }

        private IList<string> m_pathParts = new List<string>();
        public IList<string> PathParts
        {
            get { return m_pathParts; }
            set { m_pathParts = value; }
        }

        public override string ToString()
        {
            return String.Format("//{0}", this.Provider.Name) + this.LocalPath; 
        }

        public StatePath Plus(string key)
        {
            StatePath newPath = new StatePath(this);
            newPath.PathParts.Add(key);
            return newPath;
        }
   }
}

VB

'Error: Converting Methods, Functions and Constructors 
'Error: Converting If-Else-End If Blocks 

'Error: Converting For-Loops 

 
 
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports UBR.Products.State.Lib.Provider
Imports UBR.Products.State.Lib.Interfaces
Namespace UBR.Products.State.Lib.Provider
    Public Class StatePath
        Public  void New() 
        {
        }
        Public  void New(String path)
        {
            FromString(path)
        } 
 
        Public  void New(void New path)
        {
            Me.Provider = path.Provider
            Dim part As String
            For Each part In path.PathParts) Me.PathParts.Add(part
            foreach (String part in path.PathParts) Me.PathParts.Add(part)
            Next
        }
 
        Public void FromString(String path)
        {
            ' Strip off the 1st part
            if (Not path.StartsWith("//")) Throw New Exception("Path must start with //")
            Default Item.PathParts = Property List<string>(char(As path.Split(New) As New
            End Property
, StringSplitOptions.RemoveEmptyEntries))
            ' If there's at least 1 node, then set the provider and remove that node...
            if (Me.PathParts.Count > 0)
            {
                Me.Provider = SPM.Providers(Me.PathParts(0))
                Me.PathParts.RemoveAt(0)
            }
        }
 
        Public ReadOnly Property LocalPath() As String
            Get 
                Dim sb As StringBuilder =  New StringBuilder() 
                Dim part As String
                For Each part In Me.PathParts) sb.AppendFormat("/{0}",part
                foreach (String part in Me.PathParts) sb.AppendFormat("/{0}", part)
                Next
                Return sb.ToString()
            End Get
        End Property
 
        Public ReadOnly Property RootNode() As Boolean
            Get 
                 Return Me.PathParts.Count  =  0
            End Get
        End Property
 
        Public Property Name() As String
            Get 
                if CType(Return Me.Provider.Name, Me.RootNode)
                else Return Me.PathParts(Me.PathParts.Count - 1)
            End Get
            Set (ByVal Value As String) 
                Throw New Exception("Name can not be altered at Me time...")
            End Set
        End Property
 
        Public ReadOnly Property ParentPath() As StatePath
            Get 
                if CType(Return Nothing, Me.RootNode)
                else
                {
                    Dim parentPath As StatePath =  New StatePath(Me) 
                    parentPath.PathParts.RemoveAt(parentPath.PathParts.Count - 1)
                    Return parentPath
                }
            End Get
        End Property
 
        Public  void New(IStateNode node)
        {
            FromStateNode(node)
        }
 
        private void FromStateNode(IStateNode node)
        {
            if (node.Parent = Nothing)
            {
                Me.Provider = SPM.Providers(node.Name)
            }
            else
            {
                Me.Provider = node.Parent.Path.Provider
                Dim part As String
                For Each part In node.Parent.Path.PathParts) Me.PathParts.Add(part
                foreach (String part in node.Parent.Path.PathParts) Me.PathParts.Add(part)
                Next
                Me.PathParts.Add(node.Name)
            }
        }
 
        Private m_provider As StateProviderBase =  SPM.Provider 
        Public Property Provider() As StateProviderBase
            Get 
                 Return m_provider
            End Get
            Set (ByVal Value As StateProviderBase) 
                 m_provider = value
            End Set
        End Property
 
        Private m_pathParts As IList<string> =  New List<string>() 
        Public Property PathParts() As IList<string>
            Get 
                 Return m_pathParts
            End Get
            Set (ByVal Value As IList<string>) 
                 m_pathParts = value
            End Set
        End Property
 
        Public override String ToString()
        {
            Return String.Format("//{0}", Me.Provider.Name) + Me.LocalPath 
        }
 
        Public  void New Plus(String key)
        {
            Dim NewPath As StatePath =  New StatePath(Me) 
            NewPath.PathParts.Add(key)
            Return NewPath
        }
    End Class
End Namespace

The StatePath class works in paralell with the StateNode class which is described below.  The StateNode class encapsulates a StatePath object and provides the API to Get and Set the "value" associated with that node of the State Tree. 

StateNode class

The StateNode class contains fields that describe what type the node is, as well as what data is associated with that node.  It also has "helper" properties and methods that do things like return a StateNode for it's parent, or a collection of nodes the represent the Children of the specified node - but these methods are really just passthrough methods that turn right around and call methods on the provider to make it all work.

CSharp



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UBR.Products.State.Lib.Interfaces;
using UBR.Products.State.Lib.Provider; 

namespace UBR.Products.State.Lib.Provider
{
    public class StateNode : IStateNode
    {
        public StateNode(StatePath path)
        {
            this.Path = path; 

            // Now make sure that this object (and all of it's parents actually exist in the database
            IStateNode parent = this.Parent;
            while (parent != null) parent = parent.Parent;
        } 

        #region IStateNode Members
        private StateNodeType m_nodeType;
        public StateNodeType NodeType
        {
            get { return m_nodeType; }
            set { m_nodeType = value; }
        } 

        private StatePath m_path;
        public StatePath Path
        {
            get
            {
                if (this.m_path == null) throw new Exception("Path can't be null...");
                else return m_path;
            }
            set { m_path = value; }
        } 

        public StateProviderBase Provider
        {
            get { return this.Path.Provider; }
        } 

        public delegate void ValueChangingEvent(IStateNode sender, ValueChangingArgs args); 

        public static event ValueChangingEvent ValueChanging; 

        public void SetKey()
        {
            // Pass the store node call to the provider
            this.Provider.SetKey(this);
        } 

        public virtual string Name
        {
            get { return this.Path.Name; }
            set { this.Path.Name = value; }
        } 

        public IStateNode Parent
        {
            get
            {
                if (this.Path.ParentPath == null) return null;
                return SPM.GetKey(this.Path.ParentPath);
            }
        } 

        public virtual IStateNode GetChild(string key)
        {
            return this.Provider.GetKey(this.Path.Plus(key));
        } 

        private object m_value;
        public object Value
        {
            get { return m_value; }
            set
            {
                if (m_value != value)
                {
                    ValueChangingArgs args = new ValueChangingArgs(value);
                    if (ValueChanging != null) ValueChanging(this, args);
                    if (args.Result == ValueChangingResults.Continue)
                    {
                        // Set the type based on the value supplied
                        if (value is int) this.NodeType = StateNodeType.Int32;
                        else if (value == null) this.NodeType = StateNodeType.Key;
                        else if (value is DateTime) this.NodeType = StateNodeType.DateTime;
                        else if (value is string) this.NodeType = StateNodeType.String;
                        else if (value is Guid) this.NodeType = StateNodeType.Guid;
                        else if (value is Int64) this.NodeType = StateNodeType.Int64;
                        else if (value is Double) this.NodeType = StateNodeType.Double;
                        else if (value is Boolean) this.NodeType = StateNodeType.Boolean;
                        else if (value is decimal) this.NodeType = StateNodeType.Decimal;
                        m_value = value;
                        if (!m_internalSet) this.SetKey();
                    }
                }
            }
        } 

        bool m_internalSet = false;
        public void InternalSetValue(object value) {
            try
            {
                m_internalSet = true;
                this.Value = value;
            }
            finally
            {
                m_internalSet = false;
            }
        } 


        public virtual object this[string key]
        {
            get { return this.GetValue(key); }
            set { this.SetValue(key, value); }
        } 

        public virtual void SetValue(string key, object value)
        {
            this.Provider.GetKey(this.Path.Plus(key)).Value = value;
        } 

        public virtual IList<IStateNode> Children
        {
            get { return this.Provider.GetChildren(this.Path); }
        } 

        public virtual object GetValue(string key)
        {
            return GetValue<object>(key);
        } 

        public virtual T GetValue<T>(string key)
        {
            IStateNode sn = this.Provider.GetKey(this.Path.Plus(key));
            if (sn.Value == null) return default(T);
            else if (typeof(T) == typeof(bool))
            {
                if (sn.Value is bool) return (T)sn.Value;
                else if ((int)sn.Value == 0) return (T)((object)false);
                else return (T)((object)true);
            }
            else return (T)sn.Value;
        } 

        public virtual bool GetValue(string key, bool defaultValue)
        {
            try
            {
                object o = this.GetValue(key);
                if (o == null) return defaultValue;
                else return (bool)o;
            }
            catch (Exception ex)
            {
                return defaultValue;
            }
        } 

        public virtual bool HasValue(string key)
        {
            return this.GetValue(key) != null;
        } 

        public virtual string GetValue(string key, string defaultValue)
        {
            try
            {
                string temp = this.GetValue<string>(key);
                if (string.IsNullOrEmpty(temp)) return defaultValue;
                else return temp;
            }
            catch (Exception ex)
            {
                return defaultValue;
            }
        } 

        public virtual int GetValue(string key, int defaultValue)
        {
            try
            {
                if (!this.HasValue(key)) return defaultValue;
                else return this.GetValue<int>(key);
            }
            catch (Exception ex)
            {
                return defaultValue;
            }
        }
        
        public override string ToString()
        {
            return this.Path.ToString();
        } 

        #endregion
    } 

    public enum ValueChangingResults
    {
        Continue,
        Ignore
    } 

    public class ValueChangingArgs : EventArgs
    {
        public ValueChangingArgs(object newValue)
        {
            this.NewValue = newValue;
        }
        public ValueChangingResults Result;
        public object NewValue;
    }
}

VB

'Error: Converting Methods, Functions and Constructors 
'Error: Converting If-Else-End If Blocks 

 
 
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports UBR.Products.State.Lib.Interfaces
Imports UBR.Products.State.Lib.Provider 
 
Namespace UBR.Products.State.Lib.Provider
    Public Class StateNode
     Implements IStateNode
        Public  void New(StatePath path)
        {
            Me.Path = path 
 
            ' Now make sure that this object (and all of it's parents actually exist in the database
            Dim parent As IStateNode =  Me.Parent 
            While Not parent Is Nothing
            End While
        } 
 
        #region IStateNode Members
        Private m_nodeType As StateNodeType
        Public Property NodeType() As StateNodeType
            Get 
                 Return m_nodeType
            End Get
            Set (ByVal Value As StateNodeType) 
                 m_nodeType = value
            End Set
        End Property
 
        Private m_path As StatePath
        Public Property Path() As StatePath
            Get 
                if (Me.m_path = Nothing) Throw New Exception("Path can't be null...")
                else Return m_path
            End Get
            Set (ByVal Value As StatePath) 
                 m_path = value
            End Set
        End Property
 
        Public ReadOnly Property Provider() As StateProviderBase
            Get 
                 Return Me.Path.Provider
            End Get
        End Property
 
        Public delegate void ValueChangingEvent(IStateNode sender, ValueChangingArgs args) 
 
        Public static event ValueChangingEvent ValueChanging 
 
        Public void SetKey()
        {
            ' Pass the store node call to the provider
            Me.Provider.SetKey(Me)
        } 
 
        Public Overridable Property Name() As String
            Get 
                 Return Me.Path.Name
            End Get
            Set (ByVal Value As String) 
                 Me.Path.Name = value
            End Set
        End Property
 
        Public ReadOnly Property Parent() As IStateNode
            Get 
                if (Me.Path.ParentPath = Nothing) Return Nothing
                Return SPM.GetKey(Me.Path.ParentPath)
            End Get
        End Property
 
        Public virtual I void New GetChild(String key)
        {
            Return Me.Provider.GetKey(Me.Path.Plus(key))
        } 
 
        Private m_value As Object
        Public Property Value() As Object
            Get 
                 Return m_value
            End Get
            Set (ByVal Value As Object) 
                if (m_value <> value)
                {
                    Dim args As ValueChangingArgs =  New ValueChangingArgs(value) 
                    Dim ValueChangingCType(As if(ValueChanging <>  Nothing), this,args)
                    if (args.Result = ValueChangingResults.Continue)
                    {
                        ' Set the type based on the value supplied
                        if (value is Integer) Me.NodeType = StateNodeType.Int32
                        Dim if(value  =  Nothing) As else =  StateNodeType.Key 
                        else if (value is DateTime) Me.NodeType = StateNodeType.DateTime
                        else if (value is String) Me.NodeType = StateNodeType.String
                        else if (value is Guid) Me.NodeType = StateNodeType.Guid
                        else if (value is Int64) Me.NodeType = StateNodeType.Int64
                        else if (value is Double) Me.NodeType = StateNodeType.Double
                        else if (value is Boolean) Me.NodeType = StateNodeType.Boolean
                        else if (value is Decimal) Me.NodeType = StateNodeType.Decimal
                        m_value = value
                        Dim Me.SetKey() As if(Not m_internalSet)
                    }
                }
            End Set
        End Property
 
        Dim m_internalSet As Boolean =  False 
        Public void InternalSetValue(Object value) 
        {
            Try
                m_internalSet = True
                Me.Value = value
            Finally
                m_internalSet = False
            End Try
        } 
 
 
        Default Public Overridable Property Item(key As String) As Object
            Get 
                 Return Me.GetValue(key)
            End Get
            Set (ByVal Value As Object) 
                 Me.SetValue(key, value)
            End Set
        End Property
 
        Public virtual void SetValue(String key, Object value)
        {
            Me.Provider.GetKey(Me.Path.Plus(key)).Value = value
        } 
 
        Public Overridable ReadOnly Property Children() As IList<IStateNode>
            Get 
                 Return Me.Provider.GetChildren(Me.Path)
            End Get
        End Property
 
        Public virtual Object GetValue(String key)
        {
            Return GetValue<object>(key)
        } 
 
        Public virtual T GetValue<T>(String key)
        {
            Dim sn As IStateNode =  Me.Provider.GetKey(Me.Path.Plus(key)) 
            if (sn.Value = Nothing) Return default(T)
            else if (Type.GetType(T) = Type.GetType(Boolean))
            {
                if (sn.Value is Boolean) Return (T)sn.Value
                Dim if(CType(sn.Value  =  0, Integer)) As else
                Dim ReturnCType((CType(True, Object)) As else, T)
            }
            Dim ReturnCType(sn.Value As else, T)
        } 
 
        Public virtual Boolean GetValue(String key, Boolean defaultValue)
        {
            Try
                Dim o As Object =  Me.GetValue(key) 
                if (o = Nothing) Return defaultValue
                Dim ReturnCType(o As else, Boolean)
            Catch ex As Exception
                Return defaultValue
            End Try
        } 
 
        Public virtual Boolean HasValue(String key)
        {
            Return Me.GetValue(key) <> Nothing
        } 
 
        Public virtual String GetValue(String key, String defaultValue)
        {
            Try
                Dim temp As String =  Me.GetValue<string>(key) 
                if (String.IsNullOrEmpty(temp)) Return defaultValue
                else Return temp
            Catch ex As Exception
                Return defaultValue
            End Try
        } 
 
        Public virtual Integer GetValue(String key, Integer defaultValue)
        {
            Try
                if (Not Me.HasValue(key)) Return defaultValue
                else Return Me.GetValue<int>(key)
            Catch ex As Exception
                Return defaultValue
            End Try
        }
 
        Public override String ToString()
        {
            Return Me.Path.ToString()
        } 
 
        #End Region
    End Class
 
    Public Enum ValueChangingResults 
        Continue
        Ignore
    End Enum
 
    Public Class ValueChangingArgs
     Inherits EventArgs
        Public  void New(Object NewValue)
        {
            Me.NewValue = NewValue
        }
        Public Result As ValueChangingResults
        Public NewValue As Object
    End Class
End Namespace

StateProviderBase class

In order to manipulate these two classes though, we need a base abstract provider class that specifies what functionality has to be implemented by each provider.  The base class looks like this:

CSharp



using System;
using System.Configuration.Provider;
using UBR.Products.State.Lib.Interfaces;
using UBR.Products.State.Lib.Provider;
using System.Collections.Generic; 

namespace UBR.Products.State.Lib.Provider
{
    public abstract class StateProviderBase : ProviderBase
    { 

        public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
        {
            base.Initialize(name, config);
            this._name = name;
            this._description = config["description"];
        }
        protected string _name; 

        public override string Name
        {
            get { return _name; }
        }
        protected string _description; 

        public override string Description
        {
            get { return _description; }
        } 

        #region Key Management
        public abstract IStateNode GetKey(StatePath path);
        public abstract void SetKey(IStateNode node);
        public abstract IList<IStateNode> GetChildren(StatePath path);
        public virtual void Delete(StatePath path)
        {
            this.Delete(path, false);
        }
        public virtual void Delete(StatePath path, bool includeChildren)
        {
            throw new NotImplementedException("Delete not implemeneted in base class");
        }
        #endregion 

    }
}

VB

 
 
Imports System
Imports System.Configuration.Provider
Imports UBR.Products.State.Lib.Interfaces
Imports UBR.Products.State.Lib.Provider
Imports System.Collections.Generic 
 
Namespace UBR.Products.State.Lib.Provider
    Public MustInherit Class StateProviderBase
     Inherits ProviderBase 
 
        Public Overrides  Sub Initialize(ByVal name As String, ByVal config As System.Collections.Specialized.NameValueCollection)
            MyBase.Initialize(name, config)
            Me._name = name
            Me._description = config("description")
        End Sub
        Protected _name As String
 
        Public Overrides ReadOnly Property Name() As String
            Get 
                 Return _name
            End Get
        End Property
        Protected _description As String
 
        Public Overrides ReadOnly Property Description() As String
            Get 
                 Return _description
            End Get
        End Property
 
        #region Key Management
        Public abstract IStateNode GetKey(StatePath path)
        Public abstract void SetKey(IStateNode node)
        Public abstract IList<IStateNode> GetChildren(StatePath path)
        Public Overridable  Sub Delete(ByVal path As StatePath)
            Me.Delete(path, False)
        End Sub
        Public Overridable  Sub Delete(ByVal path As StatePath, ByVal includeChildren As Boolean)
            Throw New NotImplementedException("Delete not implemeneted in MyBase class")
        End Sub
        #End Region 
 
    End Class
End Namespace

 

SessionStateProvider class

The first specific provider that I implemented was the SessionProvider - because it was the easiest (at least the basic get/set was easy.

CSharp



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UBR.Products.State.Lib.Provider;
using System.Web;
using System.Web.SessionState;
using UBR.Products.State.Lib.Interfaces;
using System.Collections;

namespace UBR.Products.State.Lib.Provider
{
    class SessionStateProvider : StateProviderBase
    {
        public override IStateNode GetKey(StatePath path)
        {
            // Check if the local path exists in the session - if not, create it.
            if (Session[path.LocalPath] == null) Session[path.LocalPath] = new StateNode(path);
            return (IStateNode)Session[path.LocalPath];
        }

        private HttpSessionState Session
        {
            get
            {
                if (HttpContext.Current == null) throw new Exception("There is not HttpContext.Current...");
                else if (HttpContext.Current.Session == null) throw new Exception("There is not HttpContext.Current.Session...");
                else return HttpContext.Current.Session;
            }
        }
    }
}

VB

 
 
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports UBR.Products.State.Lib.Provider
Imports System.Web
Imports System.Web.SessionState
Imports UBR.Products.State.Lib.Interfaces
Imports System.Collections
 
Namespace UBR.Products.State.Lib.Provider
    Class SessionStateProvider
     Inherits StateProviderBase
        Public Overrides Function GetKey(ByVal path As StatePath) As IStateNode
            ' Check if the local path exists in the session - if not, create it.
            If Session(path.LocalPath) Is Nothing Then
                 Session(path.LocalPath) = New StateNode(path)
            End If
            Return CType(Session(path.LocalPath), IStateNode)
        End Function
 
        Private ReadOnly Property Session() As HttpSessionState
            Get 
                If HttpContext.Current Is Nothing Then
                     Throw New Exception("There is not HttpContext.Current...")
                Else If HttpContext.Current.Session = Nothing) Throw New Exception("There is not HttpContext.Current.Session..." Then 
                Else If else Return HttpContext.Current.Session Then 
                End If
            End Get
        End Property
    End Class
End Namespace

It's simple enough really.  If the LocalPath for the node being requested does not exist in HttpContext.Current.Session[] - then create an empty node and store it there.  In either case, return whatever is now in the session for the appropriate LocalPath.

Because a node that is returned from the session is actually being STORED in the session, there is nothing that needs to be done on "SetKey" when this value changes.  In a database environment, if the value of a node changes, then on SetKey the new value needs to be written back to the database.  In the session, no such step is needed.

To implement the GetChildren method, we need to iterate over all items in the session, and find all that are "children" of the specified path.  We do that like this:

CSharp

        public override IList<IStateNode> GetChildren(StatePath path)
        {
            IList<IStateNode> children = new List<IStateNode>();
            IList<string> childNames = new List<string>();
            // Iterate over all items in the session
            foreach (DictionaryEntry de in this.Session)
            {
                string key = de.Key.ToString();
                if (key.StartsWith(path.LocalPath)) {
                    string childName = key.Substring(path.LocalPath.Length) + "/";
                    childName = childName.Substring(0, childName.IndexOf("/"));
                    if (!childNames.Contains(childName)) childNames.Add(childName);
                }
            }
            IStateNode root = SPM.GetKey(path);
            foreach (string childName in childNames) children.Add(root.GetChild(childName));
            return children;
        }

VB

'Error: Converting Casting from C# to VB.Net 
'Error: Converting For-Loops 

        Public Overrides Function GetChildren(ByVal path As StatePath) As IList<IStateNode>
            Dim children As IList<IStateNode> =  New List<IStateNode>() 
            Dim childNames As IList<string> =  New List<string>() 
            ' Iterate over all items in the session
            Dim de As DictionaryEnTry
            For Each de In Me.Session
                Dim key As String =  de.Key.ToString() 
                If key.StartsWith(path.LocalPath) Then
                    Dim childName As String =  key.Substring(path.LocalPath.Length) + "/" 
                    childName = childName.Substring(0, childName.IndexOf("/"))
                    If Not childNames.Contains(childName) Then
                         childNames.Add(childName)
                    End If
                End If
            Next
            Dim root As IStateNode =  SPM.GetKey(path) 
            Dim childName As String
            For Each childName In childNames) children.Add(root.GetChild(childName)
            foreach (String childName in childNames) children.Add(root.GetChild(childName))
            Next
            Return children
        End Function

ApplicationStateProvider class

The "Application" provider was very similar as you can see below:

CSharp



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UBR.Products.State.Lib.Provider;
using System.Web;
using System.Web.SessionState;
using UBR.Products.State.Lib.Interfaces;
using System.Collections; 

namespace UBR.Products.State.Lib.Provider
{
    public class ApplicationStateProvider : StateProviderBase
    {
        public override IStateNode GetKey(StatePath path)
        {
            // Check if the local path exists in the session - if not, create it.
            if (Application[path.LocalPath] == null)
            {
                // If this is the root node, create a SessionRootNode, otherwise, just create a root node base
                Application[path.LocalPath] = new StateNode(path);
            }
            return (IStateNode)Application[path.LocalPath];
        } 

        public override IList<IStateNode> GetChildren(StatePath path)
        {
            IList<IStateNode> children = new List<IStateNode>();
            IList<string> childNames = new List<string>();
            // Iterate over all items in the session
            foreach (DictionaryEntry de in this.Application)
            {
                string key = de.Key.ToString();
                if (key.StartsWith(path.LocalPath))
                {
                    string childName = key.Substring(path.LocalPath.Length) + "/";
                    childName = childName.Substring(0, childName.IndexOf("/"));
                    if (!childNames.Contains(childName)) childNames.Add(childName);
                }
            }
            IStateNode root = SPM.GetKey(path);
            foreach (string childName in childNames) children.Add(root.GetChild(childName));
            return children;
        } 

        public override void SetKey(IStateNode node)
        {
            // Don't do anything. The value (along with all the other values) 
            //                                               are  already beeing stored in the session
        } 

        private HttpApplicationState Application
        {
            get
            {
                if (HttpContext.Current == null) throw new Exception("There is not HttpContext.Current...");
                else if (HttpContext.Current.Application == null) 
                               throw new Exception("There is not HttpContext.Current.Application...");
                else return HttpContext.Current.Application;
            }
        }
    }
}

VB

'Error: Converting For-Loops 

 
 
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports UBR.Products.State.Lib.Provider
Imports System.Web
Imports System.Web.SessionState
Imports UBR.Products.State.Lib.Interfaces
Imports System.Collections 
 
Namespace UBR.Products.State.Lib.Provider
    Public Class ApplicationStateProvider
     Inherits StateProviderBase
        Public Overrides Function GetKey(ByVal path As StatePath) As IStateNode
            ' Check if the local path exists in the session - if not, create it.
            If Application(path.LocalPath) Is Nothing Then
                ' If this is the root node, create a SessionRootNode, otherwise, just create a root node base
                Application(path.LocalPath) = New StateNode(path)
            End If
            Return CType(Application(path.LocalPath), IStateNode)
        End Function
 
        Public Overrides Function GetChildren(ByVal path As StatePath) As IList<IStateNode>
            Dim children As IList<IStateNode> =  New List<IStateNode>() 
            Dim childNames As IList<string> =  New List<string>() 
            ' Iterate over all items in the session
            Dim de As DictionaryEnTry
            For Each de In Me.Application
                Dim key As String =  de.Key.ToString() 
                If key.StartsWith(path.LocalPath) Then
                    Dim childName As String =  key.Substring(path.LocalPath.Length) + "/" 
                    childName = childName.Substring(0, childName.IndexOf("/"))
                    If Not childNames.Contains(childName) Then
                         childNames.Add(childName)
                    End If
                End If
            Next
            Dim root As IStateNode =  SPM.GetKey(path) 
            Dim childName As String
            For Each childName In Function children.Add(root.GetChildCType(As childNames, ByValchildName))
            ' Don't do anything. The value (along with all the other values) 
            '                                               are  already beeing stored in the session
            End Function
 
        Private ReadOnly Property Application() As HttpApplicationState
            Get 
                If HttpContext.Current Is Nothing Then
                     Throw New Exception("There is not HttpContext.Current...")
                Else If HttpContext.Current.Application Is Nothing Then 
                               Throw New Exception("There is not HttpContext.Current.Application...")
                Else If else Return HttpContext.Current.Application Then 
                End If
            End Get
        End Property
    End Class
End Namespace

Conclusion

In order to make it all work, we need a few other classes, and a few new lines in the web.config.  We need configuration classes to make the "Provider Pattern" work (you can find them in the zip file).  They are:

SPM.cs
StateConfiguration.cs
StateCollection.cs

Then - we need to tell the application about our provider infrastructure:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="StateProvider"
type="UBR.Products.State.Lib.Provider.StateConfiguration, UBR.Products.State.Lib"/>
  </configSections>
 <StateProvider>
    <providers>
      <add name="Session" type="UBR.Products.State.Lib.Provider.SessionStateProvider, UBR.Products.State.Lib"
                      description="State data persisted to the HttpContext.Current.Session (if available)" />
     <add name="Application" type="UBR.Products.State.Lib.Provider.ApplicationStateProvider, UBR.Products.State.Lib"
                      description="State data persisted to the HttpContext.Current.Application (if available)" />
   </providers>
  </StateProvider>
</configuration>

Using these classes now - we can easily do the following, from anywhere in our code:

CSharp



// Save something to the session
SPM.Session["quoteOfTheDay"] = "To be strong, and loved...";
SPM.Session.GetChild("Preferences")["ItemsPerPage"] =15;
SPM.Application.GetChild("Settings").GetChild("Display")["LastUpdated"] = DateTime.Now; 

// Then - these values can be retrieved as follows:
// where 25 is the "default" to be used if this value has not yet been set... 
Grid1.ItemsPerPage = SPM.Session.GetChild("Preferences").GetValue("ItemsPerPage", 25); 

// Or, we encapsulate the call to Get/Set the ItemsPerPage in a state object, so that our code just reads:
Grid1.ItemsPerPage = State.ItemsPerPage;

VB

 
 
' Save something to the session
SPM.Session("quoteOfTheDay") = "To be strong, and loved..."
SPM.Session.GetChild("Preferences")("ItemsPerPage") =15
SPM.Application.GetChild("Settings").GetChild("Display")("LastUpdated") = DateTime.Now 
 
' Then - these values can be retrieved as follows:
' where 25 is the "default" to be used if this value has not yet been set... 
Grid1.ItemsPerPage = SPM.Session.GetChild("Preferences").GetValue("ItemsPerPage", 25) 
 
' Or, we encapsulate the call to Get/Set the ItemsPerPage in a state object, so that our code just reads:
Grid1.ItemsPerPage = State.ItemsPerPage

As you can see, with the added benefit of hierarchy, even with just the Session and Application providers implemented it's already quite helpful.

'----------------------------------------------------------------
' Converted from C# to VB .NET using CSharpToVBConverter(1.2).
' Developed by: Kamal Patel (http://www.KamalPatel.net)
'----------------------------------------------------------------

Related Articles

Provider Pattern Part 1: A Practical Approach to Application State
Provider Pattern Part 3: The final solution

 

 


Created: 5/27/2008 | Last Updated: 5/27/2008 | broken links | helpful | not helpful | statistics
© Copyright 2006, UBR, Inc. All Rights Reserved. (788)

 

Copyright 1999-2006, All rights reserved.
Finding content
Finding content.  An error has occured...