また、Current という静的プロパティを通じて操作するようにも変更してあります。
ちなみに、最近は MVP パターン使ってるのでリンク先のような使い方はしなくなりました。Presenter をセッションに格納する際に便利です。
SessionStateAdapter<TItem> クラス
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.SessionState;
namespace MoroMoro.Enterprise.Web
{
/// <summary>
/// 型をキーとしてセッションステートを利用するアダプターです。
/// </summary>
/// <typeparam name="TItem">セッションステートに保存するインスタンスの型。</typeparam>
public class SessionStateAdapter<TItem>
where TItem : class
{
#region Static Members
/// <summary>
/// 現在のセッションステートに接続する SessionStateAdapter<TItem> オブジェクトを取得します。
/// </summary>
/// <exception cref="InvalidOperationException">現在の HttpContext を取得できませんでした。</exception>
/// <exception cref="InvalidOperationException">セッションステートが利用できません。</exception>
public static SessionStateAdapter<TItem> Current
{
get
{
HttpContext currentContext = GetCurrentContext();
object key = typeof(SessionStateAdapter<TItem>);
if (currentContext.Items[key] == null)
{
HttpSessionState currentSessionState = GetSessionState(currentContext);
currentContext.Items[key] = new SessionStateAdapter<TItem>(currentSessionState);
}
return (SessionStateAdapter<TItem>)currentContext.Items[key];
}
}
/// <summary>
/// 現在の HttpContext を取得します。
/// </summary>
/// <returns></returns>
/// <exception cref="InvalidOperationException">現在の HttpContext を取得できませんでした。</exception>
private static HttpContext GetCurrentContext()
{
HttpContext currentContext = HttpContext.Current;
if (currentContext == null)
{
throw new InvalidOperationException("現在の HttpContext を取得できませんでした。");
}
return currentContext;
}
/// <summary>
/// 現在のセッションステートを取得します。
/// </summary>
/// <returns>現在のセッションステート。</returns>
/// <exception cref="InvalidOperationException">セッションステートが利用できません。</exception>
private static HttpSessionState GetSessionState(HttpContext currentContext)
{
HttpSessionState currentSession;
try
{
currentSession = currentContext.Session;
}
catch (Exception ex)
{
throw new InvalidOperationException("セッションステートが利用できません。", ex);
}
if (currentSession == null)
{
throw new InvalidOperationException("セッションステートが利用できません。");
}
return currentSession;
}
#endregion
#region Constructors
/// <summary>
/// SessionStateAdapter<TItem> クラスの新しいインスタンスを初期化します。
/// </summary>
/// <param name="sessionState">接続するセッションステート。</param>
private SessionStateAdapter(HttpSessionState sessionState)
{
this._sessionState = sessionState;
this._key = typeof(TItem).FullName;
}
#endregion
#region Fields
/// <summary>
/// 接続するセッションステートを取得します。
/// </summary>
private readonly HttpSessionState _sessionState;
/// <summary>
/// 項目キーを取得します。
/// </summary>
private readonly string _key;
#endregion
#region Methods
/// <summary>
/// ジェネリックパラメータ TItem のインスタンスを現在のセッションステートから取得します。
/// </summary>
/// <returns>ジェネリックパラメータ TItem のインスタンス</returns>
/// <exception cref="NotFoundSessionItemException">指定した項目がセッションステート内に存在しません。</exception>
public TItem GetItem()
{
if (!ItemIsExists())
{
throw new SessionItemNotFoundException(typeof(TItem).FullName);
}
Dictionary<Type, object> holder = this.GetHolder(false);
return holder[typeof(TItem)] as TItem;
}
/// <summary>
/// ジェネリックパラメータ T のインスタンスを現在のセッションステートに設定します。
/// </summary>
/// <param name="target">ジェネリックパラメータ T のインスタンス。</param>
/// <exception cref="ArgumentNullException">引数 target が null です。</exception>
/// <exception cref="InvalidOperationException">セッションステートが読み取り専用です。</exception>
public void SetItem(TItem target)
{
if (target == null)
{
throw new ArgumentNullException("target");
}
if (this._sessionState.IsReadOnly)
{
throw this.CreateExceptionForSessionStateIsReadOnly();
}
Dictionary<Type, object> holder = this.GetHolder(true);
holder[typeof(TItem)] = target;
}
/// <summary>
/// ジェネリックパラメータ T のインスタンスを現在のセッションステートから削除します。
/// </summary>
/// <exception cref="InvalidOperationException">セッションステートが読み取り専用です。</exception>
public void RemoveItem()
{
if (this._sessionState.IsReadOnly)
{
throw this.CreateExceptionForSessionStateIsReadOnly();
}
Dictionary<Type, object> holder = this.GetHolder(true);
holder.Remove(typeof(TItem));
}
/// <summary>
/// ジェネリックパラメータ T のインスタンスが現在のセッションステートに存在するかどうかを表す値を返します。
/// </summary>
/// <returns>ジェネリックパラメータ T のインスタンスが現在のセッションステートに存在するかどうかを表す値。</returns>
/// <exception cref="InvalidOperationException">セッションステートが利用できません。</exception>
public bool ItemIsExists()
{
Dictionary<Type, object> holder = this.GetHolder(false);
if (holder == null)
{
return false;
}
if (!holder.ContainsKey(typeof(TItem)))
{
return false;
}
return (holder[typeof(TItem)] is TItem);
}
/// <summary>
/// ホルダーを取得します。
/// </summary>
/// <remarks>
/// ASP.NET 状態サービスを使用する場合、シリアル化の際に、セッション状態の各項目との間でオブジェクトグラフが共有されません。
/// これを回避するために、セッション状態にホルダー (Dictionary<TKey, Tvalue> オブジェクト) を一つ用意し、
/// セッション状態に格納する全てのオブジェクトをこのホルダーに格納します。
/// </remarks>
/// <returns>ホルダー。</returns>
private Dictionary<Type, object> GetHolder(bool createIfDoesNotExist)
{
Dictionary<Type, object> holder = this._sessionState[typeof(SessionStateAdapter<>).FullName] as Dictionary<Type, object>;
if ((holder == null) && createIfDoesNotExist)
{
if (this._sessionState.IsReadOnly)
{
throw this.CreateExceptionForSessionStateIsReadOnly();
}
holder = new Dictionary<Type, object>();
this._sessionState[typeof(SessionStateAdapter<>).FullName] = holder;
}
return holder;
}
/// <summary>
/// セッションステートが読み取り専用であることを示す例外を生成します。
/// </summary>
/// <returns>セッションステートが読み取り専用であることを示す例外。</returns>
private Exception CreateExceptionForSessionStateIsReadOnly()
{
return new InvalidOperationException("セッションステートが読み取り専用です。");
}
#endregion
}
}
SessionItemNotFoundException クラス
using System;
using System.Runtime.Serialization;
using System.Security.Permissions;
namespace MoroMoro.Enterprise.Web
{
/// <summary>
/// セッションステート内に特定の項目が見つからないことを表す例外。
/// </summary>
[Serializable]
public class SessionItemNotFoundException : Exception
{
#region Constructors
/// <summary>
/// SessionItemNotFoundException クラスの新しいインスタンスを初期化します。
/// </summary>
public SessionItemNotFoundException()
: this(null)
{
}
/// <summary>
/// この例外の原因である項目のキーを指定して、SessionItemNotFoundException クラスの新しいインスタンスを初期化します。
/// </summary>
/// <param name="key">例外の原因となった項目キー。</param>
public SessionItemNotFoundException(string key)
: this(key, "指定した項目がセッションステート内に存在しません。")
{
}
/// <summary>
/// エラー メッセージ、およびこの例外の原因である項目のキーを指定して、SessionItemNotFoundException クラスの新しいインスタンスを初期化します。
/// </summary>
/// <param name="key">例外の原因となった項目キー。</param>
/// <param name="message">エラーを説明するメッセージ。</param>
public SessionItemNotFoundException(string key, string message)
: this(key, message, null)
{
}
/// <summary>
/// エラー メッセージ、項目キー、およびこの例外の原因である内部例外への参照を使用して、SessionItemNotFoundException クラスの新しいインスタンスを初期化します。
/// </summary>
/// <param name="key">例外の原因となった項目キー。</param>
/// <param name="message">例外の原因を説明するエラー メッセージ。</param>
/// <param name="innerException">現在の例外の原因である例外。内部例外が指定されていない場合は、null 参照 (Visual Basic の場合は Nothing)。</param>
public SessionItemNotFoundException(string key, string message, Exception innerException)
: base(message, innerException)
{
this._key = key;
}
/// <summary>
/// シリアル化したデータを使用して、SessionItemNotFoundException クラスの新しいインスタンスを初期化します。
/// </summary>
/// <param name="context">転送元または転送先に関するコンテキスト情報を含んでいる System.Runtime.Serialization.StreamingContext。</param>
/// <param name="info">スローされている例外に関するシリアル化済みオブジェクト データを保持している System.Runtime.Serialization.SerializationInfo。</param>
/// <exception cref="System.Runtime.Serialization.SerializationException">クラス名が null であるか、または System.Exception.HResult が 0 です。</exception>
/// <exception cref="System.ArgumentNullException">info パラメータが null です。</exception>
protected SessionItemNotFoundException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
this._key = info.GetString("key");
}
#endregion
#region Fields
/// <summary>
/// 例外の原因となった項目キーを取得します。
/// </summary>
private readonly string _key;
#endregion
#region Properties
/// <summary>
/// 例外の原因となった項目キーを取得します。
/// </summary>
public string Key
{
get
{
return this._key;
}
}
#endregion
#region Methods
/// <summary>
/// パラメータ名と追加の例外情報を使用して System.Runtime.Serialization.SerializationInfo オブジェクトを設定します。
/// </summary>
/// <param name="context">転送元または転送先に関するコンテキスト情報。</param>
/// <param name="info">シリアル化されたオブジェクト データを保持するオブジェクト。</param>
/// <exception cref="System.ArgumentNullException">info オブジェクトが null 参照 (Visual Basic の場合は Nothing) です。</exception>
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
if (info == null)
{
throw new ArgumentNullException("info");
}
base.GetObjectData(info, context);
// 追加の例外情報がある場合、ここでパラメータ名と例外情報を info に追加します。
info.AddValue("key", this._key, typeof(string));
}
#endregion
}
}
テストケース (NUnit 2.4.8)
#pragma warning disable 1591
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.Hosting;
using System.Web.SessionState;
using NUnit.Framework;
using NUnit.Framework.SyntaxHelpers;
namespace MoroMoro.Enterprise.Web
{
[TestFixture]
[EditorBrowsable(EditorBrowsableState.Never)]
public class SessionStateAdapterTest
{
#region HttpContext Dummy
private void BuildHttpContext(bool sessionStateIsReadOnly, SimpleSessionStateItemCollection collection)
{
SimpleWorkerRequest workerRequest = new SimpleWorkerRequest("", "", "", "", null);
HttpContext.Current = new HttpContext(workerRequest);
if (collection != null)
{
HttpSessionStateContainer container =
new HttpSessionStateContainer("", collection, null, 1, true, HttpCookieMode.UseCookies, SessionStateMode.Custom, sessionStateIsReadOnly);
SessionStateUtility.AddHttpSessionStateToContext(HttpContext.Current, container);
}
}
private void BuildInvalidHttpContext()
{
SimpleWorkerRequest workerRequest = new SimpleWorkerRequest("", "", "", "", null);
HttpContext.Current = new HttpContext(workerRequest);
HttpContext.Current.Items.Add("AspSession", new object());
}
private sealed class SimpleSessionStateItemCollection : NameObjectCollectionBase, ISessionStateItemCollection
{
#region ISessionStateItemCollection メンバ
public void Clear()
{
base.BaseClear();
}
public bool Dirty
{
get;
set;
}
public NameObjectCollectionBase.KeysCollection Keys
{
get
{
return base.Keys;
}
}
public void Remove(string name)
{
base.BaseRemove(name);
}
public void RemoveAt(int index)
{
base.BaseRemoveAt(index);
}
public object this[int index]
{
get
{
return base.BaseGet(index);
}
set
{
base.BaseSet(index, value);
}
}
public object this[string name]
{
get
{
return base.BaseGet(name);
}
set
{
base.BaseSet(name, value);
}
}
#endregion
#region ICollection メンバ
public void CopyTo(Array array, int index)
{
((ICollection)this).CopyTo(array, index);
}
public int Count
{
get
{
return base.Count;
}
}
public bool IsSynchronized
{
get
{
return ((ICollection)this).IsSynchronized;
}
}
public object SyncRoot
{
get
{
return ((ICollection)this).SyncRoot;
}
}
#endregion
#region IEnumerable メンバ
public IEnumerator GetEnumerator()
{
return base.GetEnumerator();
}
#endregion
}
#endregion
[TearDown]
public void TearDown()
{
HttpContext.Current = null;
}
[Test]
public void 現在のHttpContextのインスタンスが再生成されるとSessionStateAdapterも再生成されること()
{
SimpleSessionStateItemCollection collection = new SimpleSessionStateItemCollection();
BuildHttpContext(false, collection);
SessionStateAdapter<object> adapter1 = SessionStateAdapter<object>.Current;
Assert.That(adapter1, Is.EqualTo(SessionStateAdapter<object>.Current));
BuildHttpContext(false, collection);
SessionStateAdapter<object> adapter2 = SessionStateAdapter<object>.Current;
Assert.That(adapter1, Is.Not.EqualTo(adapter2));
}
[Test]
public void 現在のHttpContextのインスタンスがNullの場合エラーになること()
{
bool wasThrown = false;
try
{
SessionStateAdapter<object> adapter1 = SessionStateAdapter<object>.Current;
}
catch (InvalidOperationException)
{
wasThrown = true;
}
Assert.That(wasThrown, Is.True);
}
[Test]
public void 現在のセッションステートがNullの場合エラーになること()
{
BuildHttpContext(false, null);
bool wasThrown = false;
try
{
SessionStateAdapter<object> adapter1 = SessionStateAdapter<object>.Current;
}
catch (InvalidOperationException)
{
wasThrown = true;
}
Assert.That(wasThrown, Is.True);
}
[Test]
public void 現在のセッションステートが正常に取得できない場合エラーになること()
{
BuildInvalidHttpContext();
bool wasThrown = false;
try
{
SessionStateAdapter<object> adapter1 = SessionStateAdapter<object>.Current;
}
catch (InvalidOperationException)
{
wasThrown = true;
}
Assert.That(wasThrown, Is.True);
}
[Test]
public void 項目を設定及び取得できること()
{
BuildHttpContext(false, new SimpleSessionStateItemCollection());
SessionStateAdapter<object> adapter = SessionStateAdapter<object>.Current;
object obj = new object();
adapter.SetItem(obj);
Assert.That(adapter.GetItem(), Is.EqualTo(obj));
}
[Test]
public void 項目を削除できること()
{
BuildHttpContext(false, new SimpleSessionStateItemCollection());
SessionStateAdapter<object> adapter = SessionStateAdapter<object>.Current;
adapter.SetItem(new object());
adapter.RemoveItem();
Assert.That(adapter.ItemIsExists(), Is.False);
}
[Test]
public void 項目の有無を確認できること()
{
BuildHttpContext(false, new SimpleSessionStateItemCollection());
SessionStateAdapter<object> adapter = SessionStateAdapter<object>.Current;
Assert.That(adapter.ItemIsExists(), Is.False);
adapter.SetItem(new object());
Assert.That(adapter.ItemIsExists(), Is.True);
adapter.RemoveItem();
Assert.That(adapter.ItemIsExists(), Is.False);
}
[Test]
public void 項目が存在しない場合項目の取得時にエラーになること()
{
BuildHttpContext(false, new SimpleSessionStateItemCollection());
SessionStateAdapter<object> adapter = SessionStateAdapter<object>.Current;
bool wasThrown = false;
try
{
adapter.GetItem();
}
catch (SessionItemNotFoundException)
{
wasThrown = true;
}
Assert.That(wasThrown, Is.True);
}
[Test]
public void 項目にNullを設定しようとするとエラーになること()
{
BuildHttpContext(false, new SimpleSessionStateItemCollection());
SessionStateAdapter<object> adapter = SessionStateAdapter<object>.Current;
bool wasThrown = false;
try
{
adapter.SetItem(null);
}
catch (ArgumentNullException)
{
wasThrown = true;
}
Assert.That(wasThrown, Is.True);
}
[Test]
public void セッションステートが読み取り専用の場合項目の設定時にエラーになること()
{
BuildHttpContext(true, new SimpleSessionStateItemCollection());
SessionStateAdapter<object> adapter = SessionStateAdapter<object>.Current;
bool wasThrown = false;
try
{
adapter.SetItem(new object());
}
catch (InvalidOperationException)
{
wasThrown = true;
}
Assert.That(wasThrown, Is.True);
}
[Test]
public void セッションステートが読み取り専用の場合項目の削除時にエラーになること()
{
SimpleSessionStateItemCollection collection = new SimpleSessionStateItemCollection();
BuildHttpContext(false, collection);
SessionStateAdapter<object> adapter1 = SessionStateAdapter<object>.Current;
adapter1.SetItem(new object());
BuildHttpContext(true, collection);
SessionStateAdapter<object> adapter2 = SessionStateAdapter<object>.Current;
bool wasThrown = false;
try
{
adapter2.RemoveItem();
}
catch (InvalidOperationException)
{
wasThrown = true;
}
Assert.That(wasThrown, Is.True);
}
}
}
#pragma warning restore 1591
トラックバックURL↓
http://csharper.blog57.fc2.com/tb.php/243-f6500658