Synchronized Hashtable that, unlike hashtable created using the native Hashtable.Synchronized method - synchronizes reads from the underlying hashtable in addition to writes.
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections;
using System.Collections.Specialized;
using Spring.Util;
namespace Spring.Collections
{
/// <summary>
/// Synchronized <see cref="Hashtable"/> that, unlike hashtable created
/// using <see cref="Hashtable.Synchronized"/> method, synchronizes
/// reads from the underlying hashtable in addition to writes.
/// </summary>
/// <remarks>
/// <p>
/// In addition to synchronizing reads, this implementation also fixes
/// IEnumerator/ICollection issue described at
/// http://msdn.microsoft.com/en-us/netframework/aa570326.aspx
/// (search for SynchronizedHashtable for issue description), by implementing
/// <see cref="IEnumerator"/> interface explicitly, and returns thread safe enumerator
/// implementations as well.
/// </p>
/// <p>
/// This class should be used whenever a truly synchronized <see cref="Hashtable"/>
/// is needed.
/// </p>
/// </remarks>
/// <author>Aleksandar Seovic</author>
[Serializable]
public class SynchronizedHashtable : IDictionary, ICollection, IEnumerable, ICloneable
{
private readonly bool _ignoreCase;
private readonly Hashtable _table;
/// <summary>
/// Initializes a new instance of <see cref="SynchronizedHashtable"/>
/// </summary>
public SynchronizedHashtable()
: this(new Hashtable(), false)
{ }
/// <summary>
/// Initializes a new instance of <see cref="SynchronizedHashtable"/>
/// </summary>
public SynchronizedHashtable(bool ignoreCase)
: this(new Hashtable(), ignoreCase)
{ }
/// <summary>
/// Initializes a new instance of <see cref="SynchronizedHashtable"/>, copying initial entries from <param name="dictionary"/>
/// handling keys depending on <param name="ignoreCase"/>.
/// </summary>
public SynchronizedHashtable(IDictionary dictionary, bool ignoreCase)
{
AssertUtils.ArgumentNotNull(dictionary, "dictionary");
this._table = (ignoreCase) ? CollectionsUtil.CreateCaseInsensitiveHashtable(dictionary) : new Hashtable(dictionary);
this._ignoreCase = ignoreCase;
}
/// <summary>
/// Creates a <see cref="SynchronizedHashtable"/> instance that
/// synchronizes access to the underlying <see cref="Hashtable"/>.
/// </summary>
/// <param name="other">the hashtable to be synchronized</param>
protected SynchronizedHashtable(Hashtable other)
{
AssertUtils.ArgumentNotNull(other, "other");
this._table = other;
}
/// <summary>
/// Creates a <see cref="SynchronizedHashtable"/> wrapper that synchronizes
/// access to the passed <see cref="Hashtable"/>.
/// </summary>
/// <param name="other">the hashtable to be synchronized</param>
public static SynchronizedHashtable Wrap(Hashtable other)
{
return new SynchronizedHashtable(other);
}
///<summary>
///Gets a value indicating whether the <see cref="T:System.Collections.IDictionary"></see> object is read-only.
///</summary>
///<returns>
///true if the <see cref="T:System.Collections.IDictionary"></see> object is read-only; otherwise, false.
///</returns>
public bool IsReadOnly
{
get
{
lock (SyncRoot)
{
return _table.IsReadOnly;
}
}
}
///<summary>
///Gets a value indicating whether the <see cref="T:System.Collections.IDictionary"></see> object has a fixed size.
///</summary>
///<returns>
///true if the <see cref="T:System.Collections.IDictionary"></see> object has a fixed size; otherwise, false.
///</returns>
public bool IsFixedSize
{
get
{
lock (SyncRoot)
{
return _table.IsFixedSize;
}
}
}
///<summary>
///Gets a value indicating whether access to the <see cref="T:System.Collections.ICollection"></see> is synchronized (thread safe).
///</summary>
///<returns>
///true if access to the <see cref="T:System.Collections.ICollection"></see> is synchronized (thread safe); otherwise, false.
///</returns>
public bool IsSynchronized
{
get { return true; }
}
///<summary>
///Gets an <see cref="T:System.Collections.ICollection"></see> object containing the keys of the <see cref="T:System.Collections.IDictionary"></see> object.
///</summary>
///<returns>
///An <see cref="T:System.Collections.ICollection"></see> object containing the keys of the <see cref="T:System.Collections.IDictionary"></see> object.
///</returns>
public ICollection Keys
{
get
{
lock (SyncRoot)
{
return _table.Keys;
}
}
}
///<summary>
///Gets an <see cref="T:System.Collections.ICollection"></see> object containing the values in the <see cref="T:System.Collections.IDictionary"></see> object.
///</summary>
///<returns>
///An <see cref="T:System.Collections.ICollection"></see> object containing the values in the <see cref="T:System.Collections.IDictionary"></see> object.
///</returns>
public ICollection Values
{
get
{
lock (SyncRoot)
{
return _table.Values;
}
}
}
///<summary>
///Gets an object that can be used to synchronize access to the <see cref="T:System.Collections.ICollection"></see>.
///</summary>
///<returns>
///An object that can be used to synchronize access to the <see cref="T:System.Collections.ICollection"></see>.
///</returns>
public object SyncRoot
{
get { return _table.SyncRoot; }
}
///<summary>
///Gets the number of elements contained in the <see cref="T:System.Collections.ICollection"></see>.
///</summary>
///<returns>
///The number of elements contained in the <see cref="T:System.Collections.ICollection"></see>.
///</returns>
public int Count
{
get
{
lock (SyncRoot)
{
return _table.Count;
}
}
}
///<summary>
///Adds an element with the provided key and value to the <see cref="T:System.Collections.IDictionary"></see> object.
///</summary>
///<param name="value">The <see cref="T:System.Object"></see> to use as the value of the element to add. </param>
///<param name="key">The <see cref="T:System.Object"></see> to use as the key of the element to add. </param>
///<exception cref="T:System.ArgumentException">An element with the same key already exists in the <see cref="T:System.Collections.IDictionary"></see> object. </exception>
///<exception cref="T:System.ArgumentNullException">key is null. </exception>
///<exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.IDictionary"></see> is read-only.-or- The <see cref="T:System.Collections.IDictionary"></see> has a fixed size. </exception><filterpriority>2</filterpriority>
public void Add(object key, object value)
{
lock (SyncRoot)
{
_table.Add(key, value);
}
}
///<summary>
///Removes all elements from the <see cref="T:System.Collections.IDictionary"></see> object.
///</summary>
///<exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.IDictionary"></see> object is read-only. </exception><filterpriority>2</filterpriority>
public void Clear()
{
lock (SyncRoot)
{
_table.Clear();
}
}
///<summary>
///Creates a new object that is a copy of the current instance.
///</summary>
///<returns>
///A new object that is a copy of this instance.
///</returns>
public object Clone()
{
lock (SyncRoot)
{
return new SynchronizedHashtable(this, _ignoreCase);
}
}
///<summary>
///Determines whether the <see cref="T:System.Collections.IDictionary"></see> object contains an element with the specified key.
///</summary>
///<returns>
///true if the <see cref="T:System.Collections.IDictionary"></see> contains an element with the key; otherwise, false.
///</returns>
///<param name="key">The key to locate in the <see cref="T:System.Collections.IDictionary"></see> object.</param>
///<exception cref="T:System.ArgumentNullException">key is null. </exception><filterpriority>2</filterpriority>
public bool Contains(object key)
{
lock (SyncRoot)
{
return _table.Contains(key);
}
}
///<summary>
/// Returns, whether this <see cref="IDictionary"/> contains an entry with the specified <paramref name="key"/>.
///</summary>
///<param name="key">The key to look for</param>
///<returns><see lang="true"/>, if this <see cref="IDictionary"/> contains an entry with this <paramref name="key"/></returns>
public bool ContainsKey(object key)
{
lock (SyncRoot)
{
return _table.ContainsKey(key);
}
}
///<summary>
/// Returns, whether this <see cref="IDictionary"/> contains an entry with the specified <paramref name="value"/>.
///</summary>
///<param name="value">The value to look for</param>
///<returns><see lang="true"/>, if this <see cref="IDictionary"/> contains an entry with this <paramref name="value"/></returns>
public bool ContainsValue(object value)
{
lock (SyncRoot)
{
return _table.ContainsValue(value);
}
}
///<summary>
///Copies the elements of the <see cref="T:System.Collections.ICollection"></see> to an <see cref="T:System.Array"></see>, starting at a particular <see cref="T:System.Array"></see> index.
///</summary>
///<param name="array">The one-dimensional <see cref="T:System.Array"></see> that is the destination of the elements copied from <see cref="T:System.Collections.ICollection"></see>. The <see cref="T:System.Array"></see> must have zero-based indexing. </param>
///<param name="index">The zero-based index in array at which copying begins. </param>
///<exception cref="T:System.ArgumentNullException">array is null. </exception>
///<exception cref="T:System.ArgumentException">The type of the source <see cref="T:System.Collections.ICollection"></see> cannot be cast automatically to the type of the destination array. </exception>
///<exception cref="T:System.ArgumentOutOfRangeException">index is less than zero. </exception>
///<exception cref="T:System.ArgumentException">array is multidimensional.-or- index is equal to or greater than the length of array.-or- The number of elements in the source <see cref="T:System.Collections.ICollection"></see> is greater than the available space from index to the end of the destination array. </exception><filterpriority>2</filterpriority>
public void CopyTo(Array array, int index)
{
lock (SyncRoot)
{
_table.CopyTo(array, index);
}
}
///<summary>
///Returns an <see cref="T:System.Collections.IDictionaryEnumerator"></see> object for the <see cref="T:System.Collections.IDictionary"></see> object.
///</summary>
///<returns>
///An <see cref="T:System.Collections.IDictionaryEnumerator"></see> object for the <see cref="T:System.Collections.IDictionary"></see> object.
///</returns>
public IDictionaryEnumerator GetEnumerator()
{
lock (SyncRoot)
{
return new SynchronizedDictionaryEnumerator(SyncRoot, _table.GetEnumerator());
}
}
///<summary>
///Removes the element with the specified key from the <see cref="T:System.Collections.IDictionary"></see> object.
///</summary>
///<param name="key">The key of the element to remove. </param>
///<exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.IDictionary"></see> object is read-only.-or- The <see cref="T:System.Collections.IDictionary"></see> has a fixed size. </exception>
///<exception cref="T:System.ArgumentNullException">key is null. </exception><filterpriority>2</filterpriority>
public void Remove(object key)
{
lock (SyncRoot)
{
_table.Remove(key);
}
}
///<summary>
///Returns an enumerator that iterates through a collection.
///</summary>
///<returns>
///An <see cref="T:System.Collections.IEnumerator"></see> object that can be used to iterate through the collection.
///</returns>
IEnumerator IEnumerable.GetEnumerator()
{
lock (SyncRoot)
{
return new SynchronizedEnumerator(SyncRoot, ((IEnumerable)_table).GetEnumerator());
}
}
///<summary>
///Gets or sets the element with the specified key.
///</summary>
///<returns>
///The element with the specified key.
///</returns>
///<param name="key">The key of the element to get or set. </param>
///<exception cref="T:System.NotSupportedException">The property is set and the <see cref="T:System.Collections.IDictionary"></see> object is read-only.-or- The property is set, key does not exist in the collection, and the <see cref="T:System.Collections.IDictionary"></see> has a fixed size. </exception>
///<exception cref="T:System.ArgumentNullException">key is null. </exception><filterpriority>2</filterpriority>
public object this[object key]
{
get
{
lock (SyncRoot)
{
return _table[key];
}
}
set
{
lock (SyncRoot)
{
_table[key] = value;
}
}
}
}
}
// ----
//
// SynchronizedEnumerator
// source: https://github.com/spring-projects/spring-net/blob/master/src/Spring/Spring.Core/Collections/SynchronizedEnumerator.cs
//
using System.Collections;
namespace Spring.Collections
{
/// <summary>
/// Synchronized <see cref="IEnumerator"/> that should be returned by synchronized
/// collections in order to ensure that the enumeration is thread safe.
/// </summary>
/// <author>Aleksandar Seovic</author>
internal class SynchronizedEnumerator : IEnumerator
{
protected object syncRoot;
protected IEnumerator enumerator;
public SynchronizedEnumerator(object syncRoot, IEnumerator enumerator)
{
this.syncRoot = syncRoot;
this.enumerator = enumerator;
}
public bool MoveNext()
{
lock (syncRoot)
{
return enumerator.MoveNext();
}
}
public void Reset()
{
lock (syncRoot)
{
enumerator.Reset();
}
}
public object Current
{
get
{
lock (syncRoot)
{
return enumerator.Current;
}
}
}
}
}
// ----
//
// Case Insensitive Hashtable
// source: https://github.com/spring-projects/spring-net/blob/master/src/Spring/Spring.Core/Collections/CaseInsensitiveHashtable.cs
//
using System;
using System.Collections;
using System.Globalization;
using System.Runtime.Serialization;
using Spring.Util;
namespace Spring.Collections
{
/// <summary>
/// Provides a performance improved hashtable with case-insensitive (string-only! based) key handling.
/// </summary>
/// <author>Erich Eichinger</author>
[Serializable]
public class CaseInsensitiveHashtable : Hashtable
{
private readonly CultureInfo _culture;
/// <summary>
/// Creates a case-insensitive hashtable using <see cref="CultureInfo.CurrentCulture"/>.
/// </summary>
public CaseInsensitiveHashtable()
: this(null, CultureInfo.CurrentCulture)
{}
/// <summary>
/// Creates a case-insensitive hashtable using the given <see cref="CultureInfo"/>.
/// </summary>
/// <param name="culture">the <see cref="CultureInfo"/> to calculate the hashcode</param>
public CaseInsensitiveHashtable(CultureInfo culture)
: this(null, culture)
{}
/// <summary>
/// Creates a case-insensitive hashtable using the given <see cref="CultureInfo"/>, initially
/// populated with entries from another dictionary.
/// </summary>
/// <param name="d">the dictionary to copy entries from</param>
/// <param name="culture">the <see cref="CultureInfo"/> to calculate the hashcode</param>
public CaseInsensitiveHashtable(IDictionary d, CultureInfo culture)
{
AssertUtils.ArgumentNotNull(culture, "culture");
_culture = culture;
if (d != null)
{
IDictionaryEnumerator enumerator = d.GetEnumerator();
while (enumerator.MoveNext())
{
this.Add(enumerator.Key, enumerator.Value);
}
}
}
/// <summary>
/// Initializes a new, empty instance of the <see cref="T:System.Collections.Hashtable"></see> class that is serializable using the specified <see cref="T:System.Runtime.Serialization.SerializationInfo"></see> and <see cref="T:System.Runtime.Serialization.StreamingContext"></see> objects.
/// </summary>
/// <param name="context">A <see cref="T:System.Runtime.Serialization.StreamingContext"></see> object containing the source and destination of the serialized stream associated with the <see cref="T:System.Collections.Hashtable"></see>. </param>
/// <param name="info">A <see cref="T:System.Runtime.Serialization.SerializationInfo"></see> object containing the information required to serialize the <see cref="T:System.Collections.Hashtable"></see> object.</param>
/// <exception cref="T:System.ArgumentNullException">info is null. </exception>
protected CaseInsensitiveHashtable(SerializationInfo info, StreamingContext context) : base(info, context)
{
string cultureName = info.GetString("_cultureName");
_culture = new CultureInfo(cultureName);
AssertUtils.ArgumentNotNull(_culture, "Culture");
}
///<summary>
///Implements the <see cref="ISerializable"></see> interface and returns the data needed to serialize the <see cref="CaseInsensitiveHashtable"></see>.
///</summary>
///
///<param name="context">A <see cref="StreamingContext"></see> object containing the source and destination of the serialized stream associated with the <see cref="CaseInsensitiveHashtable"></see>. </param>
///<param name="info">A <see cref="SerializationInfo"></see> object containing the information required to serialize the <see cref="CaseInsensitiveHashtable"></see>. </param>
///<exception cref="System.ArgumentNullException">info is null. </exception>
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
info.AddValue("_cultureName", _culture.Name);
}
/// <summary>
/// Calculate the hashcode of the given string key, using the configured culture.
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
protected override int GetHash(object key)
{
if (!(key is string)) return key.GetHashCode();
return _culture.TextInfo.ToLower((string) key).GetHashCode();
}
/// <summary>
/// Compares two keys
/// </summary>
protected override bool KeyEquals(object item, object key)
{
if (!(key is string))
{
return Equals(item,key);
}
return 0==_culture.CompareInfo.Compare((string) item, (string) key, CompareOptions.IgnoreCase);
}
/// <summary>
/// Creates a shallow copy of the current instance.
/// </summary>
public override object Clone()
{
return new CaseInsensitiveHashtable(this, _culture );
}
}
}
// ----
using System.Collections;
namespace Spring.Collections
{
/// <summary>
/// Synchronized <see cref="IDictionaryEnumerator"/> that should be returned by synchronized
/// dictionary implementations in order to ensure that the enumeration is thread safe.
/// </summary>
/// <author>Aleksandar Seovic</author>
internal class SynchronizedDictionaryEnumerator : SynchronizedEnumerator, IDictionaryEnumerator
{
public SynchronizedDictionaryEnumerator(object syncRoot, IDictionaryEnumerator enumerator)
: base(syncRoot, enumerator)
{
}
protected IDictionaryEnumerator Enumerator
{
get { return (IDictionaryEnumerator) enumerator; }
}
public object Key
{
get
{
lock (syncRoot)
{
return Enumerator.Key;
}
}
}
public object Value
{
get
{
lock (syncRoot)
{
return Enumerator.Value;
}
}
}
public DictionaryEntry Entry
{
get
{
lock (syncRoot)
{
return Enumerator.Entry;
}
}
}
}
}
// ----
//
// Unit Tests
// source: https://github.com/spring-projects/spring-net/blob/master/test/Spring/Spring.Core.Tests/Collections/SynchronizedHashtableTests.cs
using System;
using System.Collections;
using NUnit.Framework;
namespace Spring.Collections
{
[TestFixture]
public class SynchronizedHashtableTests
{
[Test]
public void BehavesLikeHashtable()
{
SynchronizedHashtable st = new SynchronizedHashtable();
st.Add("key", "value");
Assert.AreEqual("value", st["key"]);
st["key"] = "value2";
Assert.AreEqual("value2", st["key"]);
st["key2"] = "value3";
Assert.AreEqual("value3", st["key2"]);
try
{
st.Add("key", "value4");
Assert.Fail();
}
catch(ArgumentException) {}
Assert.AreEqual(2, st.Count);
}
[Test]
public void InitializeFromOtherCopiesValues()
{
Hashtable ht = new Hashtable();
ht["key"] = "value";
ht["key2"] = "value2";
SynchronizedHashtable st = new SynchronizedHashtable(ht, false);
Assert.AreEqual(2, st.Count);
ht.Remove("key");
Assert.AreEqual(1, ht.Count);
Assert.AreEqual(2, st.Count);
}
[Test]
public void DefaultsToCaseSensitive()
{
SynchronizedHashtable st = new SynchronizedHashtable();
st.Add("key","value");
st.Add("KEY","value");
Assert.AreEqual(2, st.Count);
}
[Test]
public void IgnoreCaseIgnoresCase()
{
SynchronizedHashtable st = new SynchronizedHashtable(true);
st.Add("key", "value");
Assert.AreEqual("value", st["KEY"]);
st["KeY"] = "value2";
Assert.AreEqual(1, st.Count);
Assert.AreEqual("value2", st["key"]);
try
{
st.Add("KEY", "value2");
Assert.Fail();
}
catch (ArgumentException)
{}
Hashtable ht = new Hashtable();
ht.Add("key","value");
ht.Add("KEY","value");
try
{
st = new SynchronizedHashtable(ht, true);
Assert.Fail();
}
catch (ArgumentException) {}
}
[Test]
public void WrapKeepsOriginalHashtableReference()
{
Hashtable ht = new Hashtable();
ht["key"] = "value";
ht["key2"] = "value2";
SynchronizedHashtable st = SynchronizedHashtable.Wrap(ht);
Assert.AreEqual(2, st.Count);
ht.Remove("key");
Assert.AreEqual(1, ht.Count);
Assert.AreEqual(1, st.Count);
}
/// <summary>
/// On my Notebook gives
/// Normal Hashtable: 00:00:00.0937500
/// Synced Hashtable: 00:00:00.9375000
/// =locking *has* a peformance impact.
/// </summary>
[Test, Explicit]
public void TestLockingPerformanceImpact()
{
StopWatch watch = new StopWatch();
object[] buckets = new object[10];
int iterations = 10000000;
IDictionary testDict = new Hashtable();
buckets[5] = "value";
object testResult;
using(watch.Start("Normal Hashtable: {0}"))
for(int i=0;i<iterations;i++)
{
testResult = buckets[6];
buckets[5] = "value 2";
}
testDict = new Hashtable();
testDict.Add( "key", "value" );
using(watch.Start("Synced Hashtable: {0}"))
for(int i=0;i<iterations;i++)
{
lock(buckets)
{
testResult = buckets[6];
}
lock(buckets)
{
buckets[5] = "value 2";
}
}
}
/// <summary>
/// On my Notebook gives
/// Method returning exception: 00:00:00.0156250
/// Method throwing exception: 00:00:02.1562500
/// </summary>
[Test, Explicit]
public void TestPeformanceImpactOfThrowingExceptions()
{
StopWatch watch = new StopWatch();
int iterations = 1000000;
using(watch.Start("Method returning exception: {0}"))
for(int i=0;i<iterations;i++)
{
Exception ex = MethodReturnExceptionInformation();
}
using(watch.Start("Method throwing exception: {0}"))
for(int i=0;i<iterations;i++)
{
Exception ex;
try
{
MethodThrowingException();
}
catch (Exception innerEx)
{
ex = innerEx;
}
}
}
private void MethodThrowingException()
{
throw new InvalidOperationException( "da message" );
}
private Exception MethodReturnExceptionInformation()
{
return new InvalidOperationException( "da message" );
}
}
}
// ----
//
// Example Usage
// source: https://github.com/spring-projects/spring-net/blob/master/src/Spring/Spring.Data.NHibernate5/Data/NHibernate/SimpleDelegatingSessionFactory.cs
//
using System.Collections;
using NHibernate;
using NHibernate.Cfg;
using NhCfg = NHibernate.Cfg;
using Spring.Collections;
using Spring.Threading;
using Spring.Data.Common;
using Spring.Context.Support;
using NHibernate.Engine;
using System;
namespace Spring.Data.NHibernate
{
public class SimpleDelegatingSessionFactory : DelegatingSessionFactory
{
/// <summary>
/// Connection string config element name
/// </summary>
public const string CONNECTION_STRING = "SimpleDelegatingSessionFactory.ConnectionString";
/// <summary>
/// Cache region prefix config element name
/// </summary>
public const string CACHE_REGION_PREFIX_STRING = "SimpleDelegatingSessionFactory.CacheRegionPrefix";
private Configuration _configuration;
private string _defaultConnectionString;
private object _monitor = new object();
private IDictionary _targetSessionFactories = new SynchronizedHashtable();
/// <summary>
/// public Constructor
/// </summary>
/// <param name="defaultConfiguration"></param>
public SimpleDelegatingSessionFactory(Configuration defaultConfiguration)
{
if (defaultConfiguration == null)
{
throw new ArgumentException("Configuration cannot be null", "defaultConfiguration");
}
_configuration = defaultConfiguration;
if (!_configuration.Properties.ContainsKey(NhCfg.Environment.ConnectionString))
{
throw new ArgumentException("Must specify connection string");
}
_defaultConnectionString = _configuration.Properties[NhCfg.Environment.ConnectionString] as string;
if (_defaultConnectionString == null)
{
throw new ArgumentException("Connection string property must be of type string, not " +
_configuration.Properties[NhCfg.Environment.ConnectionString].GetType().FullName);
}
}
public override ISessionFactory TargetSessionFactory
{
get
{
string connectionString = LogicalThreadContext.GetData(CONNECTION_STRING) as string;
System.Diagnostics.Trace.WriteLine(String.Format("{0} = {1}", System.Threading.Thread.CurrentThread.GetHashCode(), connectionString));
if (connectionString == null)
{
connectionString = _defaultConnectionString;
}
lock (_monitor)
{
if (!_targetSessionFactories.Contains(connectionString))
{
System.Diagnostics.Trace.WriteLine(System.Threading.Thread.CurrentThread.GetHashCode().ToString() + " = (created) ");
string originalPrefix = string.Empty;
if (_configuration.Properties.ContainsKey(NhCfg.Environment.CacheRegionPrefix))
{
originalPrefix = _configuration.Properties[NhCfg.Environment.CacheRegionPrefix];
}
string cacheRegionPrefix = LogicalThreadContext.GetData(CACHE_REGION_PREFIX_STRING) as string;
if (!string.IsNullOrEmpty(cacheRegionPrefix))
{
_configuration.Properties[NhCfg.Environment.CacheRegionPrefix] = originalPrefix + cacheRegionPrefix;
System.Diagnostics.Trace.WriteLine(String.Format("{0} = (cache region prefix) {1}", System.Threading.Thread.CurrentThread.GetHashCode(), cacheRegionPrefix));
}
_configuration.Properties[NhCfg.Environment.ConnectionString] = connectionString;
ISessionFactory sessionFactory = _configuration.BuildSessionFactory();
LocalSessionFactoryObject.DbProviderWrapper dbProviderWrapper = ((ISessionFactoryImplementor)sessionFactory).ConnectionProvider as LocalSessionFactoryObject.DbProviderWrapper;
if (dbProviderWrapper != null)
{
dbProviderWrapper.DbProvider = (IDbProvider)ContextRegistry.GetContext().GetObject("DbProvider");
}
//Reset the Cache Region Prefix to the original value
// This is so other cache region prefixes are not appended together.
_configuration.Properties[NhCfg.Environment.CacheRegionPrefix] = originalPrefix;
_targetSessionFactories[connectionString] = sessionFactory;
}
else
System.Diagnostics.Trace.WriteLine(System.Threading.Thread.CurrentThread.GetHashCode().ToString() + " = (cached) ");
ISessionFactory factory = _targetSessionFactories[connectionString] as ISessionFactory;
System.Diagnostics.Trace.WriteLine(String.Format("{0} = {1}", System.Threading.Thread.CurrentThread.GetHashCode(), connectionString));
return factory;
}
}
}
}
}