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)
| 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 classThe 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 classThe 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 classIn 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 classThe 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 classThe "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
|