Sunday, May 1, 2011

A framework for cross-cutting concerns

A multitude of frameworks can be leveraged by your application to address one or more cross-cutting application concerns (e.g. exception handling, caching, validation, etc.).  However, sometimes it behooves you to not couple your application to a specific framework (e.g. Enterprise Library, Log4Net, ELMAH).

I've decided to develop an example aspect-oriented programming (AOP) framework API to facilitate decoupling from AOP frameworks.  For each framework we wish to use for the concrete implementation of a cross-cutting concern, we'd have to develop an adapter.  I have posted below the interfaces for such a framework and, since I use Enterprise Library (EntLib) a lot, I've included the relevant adapters as well.

Disclaimer: The code below carries no guarantees-- heck, it may not even work-- so use at your own risk.

    
    public interface INotifyDisposed
    {
        event EventHandler Disposed;
    }


    public abstract class ScopeLimitedObject : IDisposable, INotifyDisposed
    {
        public event EventHandler Disposed = null;


        // We provide this version of the property name as it is most common in legacy .NET and applications.
        public bool Disposing
        { get { return IsDisposing; } }

        protected bool IsDisposing
        { get; private set; }

        public bool IsDisposed
        { get; protected set; }


        private void RaiseDisposed()
        {
            var @event = Disposed;
            if (@event != null)
                @event(this, null);
        }


        /// <summary>
        /// This is the dispose routine you want to normally handle.
        /// </summary>
        protected virtual void OnDisposeExplicit()
        { }

        /// <summary>
        /// This is the dispose routine that *typically* originates from garbage collection and where
        /// dispose was NEVER called explicitly.
        /// </summary>
        protected virtual void OnDisposeImplicit()
        { }

        /// <summary>
        /// This is a dispose routine that is always called, during both implicit and explicit calls to 
        /// Dispose.  WARNING: this method will likely (though not guaranteed) be executed twice, 
        /// so be careful of the side effects in your own overriding method.  You never need to provide
        /// an implementation of this method if you adequately provide implementations for both
        /// "OnDisposeExplicit" and "OnDisposeImplicit".
        /// </summary>
        protected virtual void OnDisposeRegardless()
        { }


        protected void Dispose(bool isDisposing)
        {
            if (!(IsDisposed || IsDisposing))
            {
                IsDisposing = true;

                if (isDisposing)
                {
                    IsDisposed = true;
                    OnDisposeExplicit();
                }
                else
                    OnDisposeImplicit();

                RaiseDisposed();
            }

            OnDisposeRegardless();
        }


        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }


        ~ScopeLimitedObject()
        { Dispose(false); }
    }


    public interface Indexable<TKey, TValue> : IEnumerable<TValue>
    {
        bool TryGet(TKey key, out TValue value);

        TValue this[TKey key]
        { get; }
    }


    public interface IndexableByName<TEntry> : Indexable<string, TEntry>
    {
        new bool TryGet(string name, out TEntry entry);

        new TEntry this[string name]
        { get; }
    }


    public interface ExceptionManager
    {
        /// <summary>
        /// For advanced scenarios (suggested you use "Process" over this method): Handles the 
        /// specified <see cref="Exception"/> object according to the rules configured for <paramref name="policyName"/>. 
        /// </summary>a
        /// <param name="exceptionToHandle">An <see cref="Exception"/> object.</param>
        /// <param name="policyName">The name of the policy to handle.</param>
        /// <param name="exceptionToThrow">The new <see cref="Exception"/> to throw, if any.</param>
        /// <remarks>
        /// If a rethrow is recommended and <paramref name="exceptionToThrow"/> is <see langword="null"/>,
        /// then the original exception <paramref name="exceptionToHandle"/> should be rethrown; otherwise,
        /// the exception returned in <paramref name="exceptionToThrow"/> should be thrown.
        /// </remarks>
        /// <returns>
        /// Whether or not a rethrow is recommended. 
        /// </returns>
        /// <example>
        /// The following code shows the usage of the 
        /// exception handling framework.
        /// <code>
        /// try
        /// {
        ///  Foo();
        /// }
        /// catch (Exception e)
        /// {
        ///     Exception exceptionToThrow;
        ///  if (exceptionManager.HandleException(e, "policy", out exceptionToThrow))
        ///  {
        ///    if(exceptionToThrow == null)
        ///      throw;
        ///    else
        ///      throw exceptionToThrow;
        ///  }
        /// }
        /// </code>
        /// </example>
        /// <seealso cref="ExceptionManager.HandleException(Exception, string)"/>
        bool HandleException(Exception exceptionToHandle, string policyName, out Exception exceptionToThrow);

        /// <summary>
        /// For advanced scenarios (suggested you use "Process" over this method): Handles the 
        /// specified <see cref="Exception"/> object according to the rules configured for <paramref name="policyName"/>. 
        /// </summary>
        /// <param name="exceptionToHandle">An <see cref="Exception"/> object.</param>
        /// <param name="policyName">The name of the policy to handle.</param>        
        /// <returns>
        /// Whether or not a rethrow is recommended.
        /// </returns>
        /// <example>
        /// The following code shows the usage of the 
        /// exception handling framework.
        /// <code>
        /// try
        /// {
        ///  Foo();
        /// }
        /// catch (Exception e)
        /// {
        ///  if (exceptionManager.HandleException(e, "policy")) throw;
        /// }
        /// </code>
        /// </example>
        /// <seealso cref="ExceptionManager.Process"/>
        bool HandleException(Exception exceptionToHandle, string policyName);

        /// <summary>
        /// Excecutes the supplied delegate <paramref name="action"/> and handles 
        /// any thrown exception according to the rules configured for <paramref name="policyName"/>.
        /// </summary>
        /// <param name="action">The delegate to execute.</param>
        /// <param name="policyName">The name of the policy to handle.</param>
        /// <param name="finallyAction">A delegate to execute as if in a "finally" block.</param>        
        /// <example>
        /// The following code shows one usage of this method.
        /// <code>
        ///  exceptionManager.Process(() => { Foo(); }, "policy", () => { CleanUpResources(); });
        /// </code>
        /// </example>
        /// <seealso cref="ExceptionManager.HandleException(Exception, string)"/>
        void Process(Action action, string policyName, Action finallyAction);

        /// <summary>
        /// Excecutes the supplied delegate <paramref name="action"/> and handles 
        /// any thrown exception according to the rules configured for <paramref name="policyName"/>.
        /// </summary>
        /// <param name="action">The delegate to execute.</param>
        /// <param name="policyName">The name of the policy to handle.</param>        
        /// <example>
        /// The following code shows one usage of this method.
        /// <code>
        ///  exceptionManager.Process(() => { Foo(); }, "policy");
        /// </code>
        /// </example>
        /// <seealso cref="ExceptionManager.HandleException(Exception, string)"/>
        void Process(Action action, string policyName);

        /// <summary>
        /// Excecutes the supplied delegate <paramref name="action"/> and handles 
        /// any thrown exception according to the rules configured for the systems default exception policy, ExceptionPolicy.Default
        /// (please note that, though calling this method may provide some desired benefit, a well-designed application will
        /// make liberal use of well-defined exception policies).
        /// </summary>
        /// <param name="action">The delegate to execute.</param>
        /// <example>
        /// The following code shows one usage of this method.
        /// <code>
        ///  exceptionManager.Process(() => { Foo(); });
        /// </code>
        /// </example>
        /// <seealso cref="ExceptionManager.HandleException(Exception, string)"/>
        void Process(Action action);
    }


    public class EntLibBasedExceptionManager : ExceptionManager
    {
        private readonly EntLibExceptionManager exceptionManager = null;


        public bool HandleException(Exception exceptionToHandle, string policyName, out Exception exceptionToThrow)
        { return exceptionManager.HandleException(exceptionToHandle, policyName, out exceptionToThrow); }

        public bool HandleException(Exception exceptionToHandle, string policyName)
        { return exceptionManager.HandleException(exceptionToHandle, policyName); }

        public void Process(Action action, string policyName, Action finallyAction)
        {
            try
            { action(); }
            catch (Exception ex)
            {
                var exToThrow = (Exception)null;
                if (HandleException(ex, policyName, out exToThrow))
                {
                    if (exToThrow == null)
                        throw;
                    else
                        throw exToThrow;
                }
            }
            finally
            {
                if (finallyAction != null)
                    Process(finallyAction, ExceptionPolicy.Default);
            }
        }

        public void Process(Action action, string policyName)
        { Process(action, policyName, null); }

        public void Process(Action action)
        { Process(action, ExceptionPolicy.Default); }


        public EntLibBasedExceptionManager(EntLibExceptionManager exceptionManager)
        {
            Guard.AgainstNull(exceptionManager, "exceptionManager");
            this.exceptionManager = exceptionManager;
        }
    }



    public abstract class CacheItem
    {
        public abstract string Key { get; }
        public abstract DateTime LastAccessedTime { get; }
        public abstract CacheItemPriority ScavengingPriority { get; }
        public abstract object Value { get; }


        public abstract bool HasExpired();
        public abstract void MakeEligibleForScavenging();        
        public abstract void MakeIneligibleForScavenging();        
    }


    /// <summary>
    /// Specifies the item priority levels.
    /// </summary>
    public enum CacheItemPriority
    {
        /// <summary>
        /// Should never be seen in nature.
        /// </summary>
        None = 0,
        /// <summary>
        /// Low priority for scavenging.
        /// </summary>
        Low = 1,
        /// <summary>
        /// Normal priority for scavenging.
        /// </summary>
        Normal,
        /// <summary>
        /// High priority for scavenging.
        /// </summary>
        High,
        /// <summary>
        /// Non-removable priority for scavenging.
        /// </summary>
        NotRemovable
    }

    
    /// <summary>
    /// Allows end users to implement their own cache item expiration schema.  Should be marked as Serializable.
    /// </summary>
    public interface CacheItemExpirationPolicy
    {
        /// <summary>
        /// Specifies if item has expired or not.
        /// </summary>
        /// <returns>Returns true if the item has expired, otherwise false.</returns>
        bool HasExpired();

        /// <summary>
        /// Called to tell the expiration that the CacheItem to which this expiration belongs has been touched by the user
        /// </summary>
        void Notify();

        /// <summary>
        /// Called to give the instance the opportunity to initialize itself from information contained in the CacheItem.
        /// </summary>
        /// <param name="owningCacheItem">CacheItem that owns this expiration policy</param>
        void Initialize(CacheItem owningCacheItem);
    }


    [Serializable]
    sealed public class AbsoluteTimeExpirationPolicy : CacheItemExpirationPolicy
    {
        private DateTime expirationDateTime;        

        
        public bool HasExpired()
        { return DateTime.Now.ToUniversalTime().Ticks >= this.expirationDateTime.Ticks; }

        public void Notify()
        { }

        public void Initialize(CacheItem owningCacheItem)
        { }


        public AbsoluteTimeExpirationPolicy(DateTime dateTime)
        {
            Guard.Against(dateTime, dt => dt <= DateTime.Now, Resources.ConstructedOrProvidedTimeCannotBeLessThanCurrentTime, "dateTime");
            this.expirationDateTime = dateTime.ToUniversalTime();
        }

        public AbsoluteTimeExpirationPolicy(TimeSpan duration) : this(DateTime.Now + duration)
        { }
    }


    [Serializable]
    sealed public class FileDependentExpirationPolicy : CacheItemExpirationPolicy
    {
        private string dependentFilePath = null;
        private DateTime dependentFileLastModifiedTime = default(DateTime);


        private static void EnsureFileIsAccessible(string filePath)
        { new FileIOPermission(FileIOPermissionAccess.Read, filePath).Demand(); }


        public bool HasExpired()
        {
            EnsureFileIsAccessible(dependentFilePath);
            return !File.Exists(dependentFilePath)
                || (DateTime.Compare(dependentFileLastModifiedTime, File.GetLastWriteTime(dependentFilePath)) != 0);
        }

        public void Notify()
        { }

        public void Initialize(CacheItem owningCacheItem)
        { }


        public FileDependentExpirationPolicy(string filePath)
        {
            Guard.AgainstNullOrEmpty(filePath, "filePath");
            EnsureFileIsAccessible(filePath);
            Guard.Against(filePath, path => !File.Exists(path), Resources.ProvidedFilePathMustExist, "filePath");

            this.dependentFilePath = Path.GetFullPath(filePath);
            this.dependentFileLastModifiedTime = File.GetLastWriteTime(filePath);
        }
    }


    [Serializable]
    sealed internal class NoExpirationPolicy : CacheItemExpirationPolicy
    {
        public bool HasExpired()
        { return false; }

        public void Notify()
        { }

        public void Initialize(CacheItem owningCacheItem)
        { }
    }


    [Serializable]
    sealed internal class SlidingTimeExpirationPolicy : CacheItemExpirationPolicy
    {
        private DateTime timeLastUsed = default(DateTime);
        private TimeSpan maxDurationOfNonUsageBeforeExpiration = default(TimeSpan);


        public bool HasExpired()
        { return DateTime.Now.ToUniversalTime().Ticks >= (timeLastUsed.ToUniversalTime().Ticks + maxDurationOfNonUsageBeforeExpiration.Ticks); }

        public void Notify()
        { timeLastUsed = DateTime.Now; }

        public void Initialize(CacheItem owningCacheItem)
        {
            Guard.AgainstNull(owningCacheItem, "owningCacheItem");
            timeLastUsed = owningCacheItem.LastAccessedTime;
        }


        public SlidingTimeExpirationPolicy(TimeSpan maxDurationOfNonUsageBeforeExpiration)
        {
            Guard.Against(
                maxDurationOfNonUsageBeforeExpiration, 
                timeSpan => timeSpan.TotalSeconds < 1,
                Resources.MaxDurationOfNonUseBeforeExpirationMustBeGreaterThanOrEqualToOneSecond,
                "maxDurationOfNonUsageBeforeExpiration"
            );

            this.maxDurationOfNonUsageBeforeExpiration = maxDurationOfNonUsageBeforeExpiration;
        }
    }


    public enum CacheItemRemovedReason
    {
        /// <summary>
        /// The item has expired.
        /// </summary>
        Expired,

        /// <summary>
        /// The item was manually removed from the cache.
        /// </summary>
        Removed,

        /// <summary>
        /// The item was removed by the scavenger because it had a lower priority that any other item in the cache.
        /// </summary>
        Scavenged,

        /// <summary>
        /// Reserved. Do not use.
        /// </summary>
        Unknown = 9999
    }

    
    /// <summary>
    /// This interface defines the contract that must be implemented to create an object that can be used to refresh 
    /// an expired item from the cache. The implementing class must be serializable. Care must be taken when implementing 
    /// this interface not to create an object that maintains too much state about its environment, as all portions of its
    /// environment will be serialized as well, creating possibly a huge object graph.
    /// </summary>
    public interface CacheItemRemovedHandler
    {
        /// <summary>
        /// Called when an item expires from the cache. This method can be used to notify an application that
        /// the expiration occured, cause the item to be refetched and refreshed from its original location, or 
        /// perform any other application-specific action. 
        /// </summary>
        /// <param name="removedKey">Key of item removed from cache. Will never be null.</param>
        /// <param name="expiredValue">Value from cache item that was just expired</param>
        /// <param name="removalReason">Reason the item was removed from the cache. See <see cref="CacheItemRemovedReason"/></param>
        /// <remarks>This method should catch and handle any exceptions thrown during its operation. No exceptions should leak
        /// out of it.</remarks>
        void OnCacheItemRemoved(string removedKey, object expiredValue, CacheItemRemovedReason removalReason);
    }


    public interface CacheManager
    {
        /// <summary>
        /// Adds new CacheItem to cache. If another item already exists with the same key, that item is removed before
        /// the new item is added. If any failure occurs during this process, the cache will not contain the item being added.
        /// </summary>
        /// <param name="key">Identifier for this CacheItem</param>
        /// <param name="value">Value to be stored in cache. May be null.</param>
        /// <param name="scavengingPriority">Specifies the new item's scavenging priority. See <see cref="CacheItemPriority" /> for more information.</param>
        /// <param name="cacheItemRemovedHandler">Object provided to allow the cache to handle the removal of an item that has been expired. May be null.</param>
        /// <param name="expirations">Param array specifying the expiration policies to be applied to this item. May be null or omitted.</param>
        /// <exception cref="ArgumentNullException">Provided key is null</exception>
        /// <exception cref="ArgumentException">Provided key is an empty string</exception>
        /// <remarks>The CacheManager can be configured to use different storage mechanisms in which to store the CacheItems.
        /// Each of these storage mechanisms can throw exceptions particular to their own implementations.</remarks>
        void Add(string key, object value, CacheItemPriority scavengingPriority, CacheItemRemovedHandler cacheItemRemovedHandler, params CacheItemExpirationPolicy[] expirations);

        /// <summary>
        /// Adds new CacheItem to cache. If another item already exists with the same key, that item is removed before
        /// the new item is added. If any failure occurs during this process, the cache will not contain the item being added.
        /// </summary>
        /// <param name="key">Identifier for this CacheItem</param>
        /// <param name="value">Value to be stored in cache. May be null.</param>
        /// <param name="cacheItemRemovedHandler">Object provided to allow the cache to handle the removal of an item that has been expired. May be null.</param>
        /// <param name="expirations">Param array specifying the expiration policies to be applied to this item. May be null or omitted.</param>
        /// <exception cref="ArgumentNullException">Provided key is null</exception>
        /// <exception cref="ArgumentException">Provided key is an empty string</exception>
        /// <remarks>The CacheManager can be configured to use different storage mechanisms in which to store the CacheItems.
        /// Each of these storage mechanisms can throw exceptions particular to their own implementations.</remarks>
        void Add(string key, object value, CacheItemRemovedHandler cacheItemRemovedHandler, params CacheItemExpirationPolicy[] expirations);

        /// <summary>
        /// Adds new CacheItem to cache. If another item already exists with the same key, that item is removed before
        /// the new item is added. If any failure occurs during this process, the cache will not contain the item being added.
        /// </summary>
        /// <param name="key">Identifier for this CacheItem</param>
        /// <param name="value">Value to be stored in cache. May be null.</param>
        /// <param name="scavengingPriority">Specifies the new item's scavenging priority. See <see cref="CacheItemPriority" /> for more information.</param>
        /// <param name="expirations">Param array specifying the expiration policies to be applied to this item. May be null or omitted.</param>
        /// <exception cref="ArgumentNullException">Provided key is null</exception>
        /// <exception cref="ArgumentException">Provided key is an empty string</exception>
        /// <remarks>The CacheManager can be configured to use different storage mechanisms in which to store the CacheItems.
        /// Each of these storage mechanisms can throw exceptions particular to their own implementations.</remarks>
        void Add(string key, object value, CacheItemPriority scavengingPriority, params CacheItemExpirationPolicy[] expirations);

        /// <summary>
        /// Adds new CacheItem to cache. If another item already exists with the same key, that item is removed before
        /// the new item is added. If any failure occurs during this process, the cache will not contain the item being added.
        /// </summary>
        /// <param name="key">Identifier for this CacheItem</param>
        /// <param name="value">Value to be stored in cache. May be null.</param>
        /// <param name="expirations">Param array specifying the expiration policies to be applied to this item. May be null or omitted.</param>
        /// <exception cref="ArgumentNullException">Provided key is null</exception>
        /// <exception cref="ArgumentException">Provided key is an empty string</exception>
        /// <remarks>The CacheManager can be configured to use different storage mechanisms in which to store the CacheItems.
        /// Each of these storage mechanisms can throw exceptions particular to their own implementations.</remarks>
        void Add(string key, object value, params CacheItemExpirationPolicy[] expirations);


        /// <summary>
        /// Returns true if key refers to item current stored in cache
        /// </summary>
        /// <param name="key">Key of item to check for</param>
        /// <returns>True if item referenced by key is in the cache</returns>
        bool Contains(string key);

        /// <summary>
        /// Returns the number of items currently in the cache.
        /// </summary>
        int Count { get; }

        /// <summary>
        /// Removes all items from the cache. If an error occurs during the removal, the cache is left unchanged.
        /// </summary>
        /// <remarks>The CacheManager can be configured to use different storage mechanisms in which to store the CacheItems.
        /// Each of these storage mechanisms can throw exceptions particular to their own implementations.</remarks>
        void Flush();

        /// <summary>
        /// Returns the value associated with the given key.
        /// </summary>
        /// <param name="key">Key of item to return from cache.</param>
        /// <returns>Value stored in cache</returns>
        /// <exception cref="ArgumentNullException">Provided key is null</exception>
        /// <exception cref="ArgumentException">Provided key is an empty string</exception>
        /// <remarks>The CacheManager can be configured to use different storage mechanisms in which to store the CacheItems.
        /// Each of these storage mechanisms can throw exceptions particular to their own implementations.</remarks>
        T Get<T>(string key);

        /// <summary>
        /// Tries to return the value associated with the given key.
        /// </summary>
        /// <param name="key">Key of item to return from cache.</param>
        /// <param name="cachedItem">The cached item.</param>
        /// <returns>whether or not the key was found</returns>
        /// <exception cref="ArgumentNullException">Provided key is null</exception>
        /// <exception cref="ArgumentException">Provided key is an empty string</exception>
        /// <remarks>The CacheManager can be configured to use different storage mechanisms in which to store the CacheItems.
        /// Each of these storage mechanisms can throw exceptions particular to their own implementations.</remarks>
        bool TryGet<T>(string key, out T cachedItem);

        /// <summary>
        /// Removes the given item from the cache. If no item exists with that key, this method does nothing.
        /// </summary>
        /// <param name="key">Key of item to remove from cache.</param>
        /// <exception cref="ArgumentNullException">Provided key is null</exception>
        /// <exception cref="ArgumentException">Provided key is an empty string</exception>
        /// <remarks>The CacheManager can be configured to use different storage mechanisms in which to store the CacheItems.
        /// Each of these storage mechanisms can throw exceptions particular to their own implementations.</remarks>
        void Remove(string key);

        /// <summary>
        /// Returns the item identified by the provided key
        /// </summary>
        /// <param name="key">Key to retrieve from cache</param>
        /// <exception cref="ArgumentNullException">Provided key is null</exception>
        /// <exception cref="ArgumentException">Provided key is an empty string</exception>
        /// <remarks>The CacheManager can be configured to use different storage mechanisms in which to store the cache items.
        /// Each of these storage mechanisms can throw exceptions particular to their own implementations.</remarks>
        object this[string key] { get; }
    }


    public interface CacheManagers
    {
        CacheManager GetDefault();
        CacheManager Get(string name);
    }


    sealed public class ExtendableCacheItemExpirationPolicyResolver
    { }
    
    
    public static class CachedItem
    {
        public static readonly ExtendableCacheItemExpirationPolicyResolver Expires = new ExtendableCacheItemExpirationPolicyResolver();
    }


    internal static class CacheItemPriorityExtensions
    {
        public static EntLibCacheItemPriority ToEntLib(this CacheItemPriority priority)
        { return (EntLibCacheItemPriority)((int)priority); }

        public static CacheItemPriority ToFramework(this EntLibCacheItemPriority priority)
        { return (CacheItemPriority)((int)priority); }
    }


    internal static class CacheItemRemovedReasonExtensions
    {
        public static EntLibCacheItemRemovedReason ToEntLib(this CacheItemRemovedReason reason)
        { return (EntLibCacheItemRemovedReason)((int)reason); }

        public static CacheItemRemovedReason ToFramework(this EntLibCacheItemRemovedReason reason)
        { return (CacheItemRemovedReason)((int)reason); }
    }


    [Serializable]
    sealed internal class EntLibCacheItemExpirationWrapper : ICacheItemExpiration
    {
        private CacheItemExpirationPolicy policy = null;


        public bool HasExpired()
        { return policy.HasExpired(); }

        public void Initialize(EntLibCacheItem owningCacheItem)
        { policy.Initialize(new EntLibCacheItemWrapper(owningCacheItem)); }

        public void Notify()
        { policy.Notify(); }


        public EntLibCacheItemExpirationWrapper(CacheItemExpirationPolicy policy)
        {
            Guard.AgainstNull(policy, "policy");
            this.policy = policy;
        }
    }


    [Serializable]
    sealed internal class EntLibCacheItemRemovedHandlerWrapper : ICacheItemRefreshAction
    {
        private CacheItemRemovedHandler removedHandler = null;


        public void Refresh(string removedKey, object expiredValue, EntLibCacheItemRemovedReason removalReason)
        { removedHandler.OnCacheItemRemoved(removedKey, expiredValue, removalReason.ToFramework()); }


        public EntLibCacheItemRemovedHandlerWrapper(CacheItemRemovedHandler removedHandler)
        {
            Guard.AgainstNull(removedHandler, "removedHandler");
            this.removedHandler = removedHandler;
        }
    }


    sealed internal class EntLibCacheItemWrapper : CacheItem
    {
        private readonly EntLibCacheItem cacheItem = null;

        internal EntLibCacheItem WrappedCacheItem
        { get { return cacheItem; } }
        
        public override string Key
        { get { return cacheItem.Key; } }

        public override DateTime LastAccessedTime
        { get { return cacheItem.LastAccessedTime; } }

        public override CacheItemPriority ScavengingPriority
        { get { return cacheItem.ScavengingPriority.ToFramework(); } }

        public override object Value
        { get { return cacheItem.Value; } }


        public override bool HasExpired()
        { return cacheItem.HasExpired(); }

        public override void MakeEligibleForScavenging()
        { cacheItem.MakeEligibleForScavenging(); }

        public override void MakeIneligibleForScavenging()
        { cacheItem.MakeNotEligibleForScavenging(); }


        internal EntLibCacheItemWrapper(EntLibCacheItem cacheItem)
        {
            Guard.AgainstNull(cacheItem, "cacheItem");
            this.cacheItem = cacheItem;
        }
    }


    sealed internal class EntLibCacheManagerAdapter : CacheManager
    {
        private readonly ICacheManager cacheManager = null;
        private readonly ICacheItemExpiration[] EmptyExpirations = new ICacheItemExpiration[] { };


        public void Add(string key, object value, CacheItemPriority scavengingPriority, CacheItemRemovedHandler cacheItemRemovedHandler, params CacheItemExpirationPolicy[] expirations)
        {
            cacheManager.Add(
                key,
                value,
                scavengingPriority.ToEntLib(),
                cacheItemRemovedHandler == null ? null : new EntLibCacheItemRemovedHandlerWrapper(cacheItemRemovedHandler),
                expirations == null ? EmptyExpirations : expirations.Select(expiration => new EntLibCacheItemExpirationWrapper(expiration)).ToArray()
            );
        }

        public void Add(string key, object value, CacheItemRemovedHandler cacheItemRemovedHandler, params CacheItemExpirationPolicy[] expirations)
        { Add(key, value, CacheItemPriority.Normal, cacheItemRemovedHandler, expirations); }

        public void Add(string key, object value, CacheItemPriority scavengingPriority, params CacheItemExpirationPolicy[] expirations)
        { Add(key, value, scavengingPriority, null, expirations); }

        public void Add(string key, object value, params CacheItemExpirationPolicy[] expirations)
        { Add(key, value, (CacheItemRemovedHandler) null, expirations); }
        

        public bool Contains(string key)
        { return cacheManager.Contains(key); }

        public int Count
        { get { return cacheManager.Count; } }

        public void Flush()
        { cacheManager.Flush(); }

        public T Get<T>(string key)
        { return (T) cacheManager.GetData(key); }

        public bool TryGet<T>(string key, out T cachedItem)
        {
            var couldOrNot = Contains(key);
            cachedItem = couldOrNot ? Get<T>(key) : default(T);
            return couldOrNot;
        }

        public void Remove(string key)
        { cacheManager.Remove(key); }

        public object this[string key]
        { get { return cacheManager[key]; } }


        public EntLibCacheManagerAdapter(ICacheManager cacheManager)
        { this.cacheManager = cacheManager; }
    }


    sealed internal class EntLibCacheManagersAdapter : CacheManagers
    {
        private readonly Components components = null;


        public CacheManager GetDefault()
        { return new EntLibCacheManagerAdapter(components.Get<EntLibCacheManager>()); }

        public CacheManager Get(string name)
        { return new EntLibCacheManagerAdapter(components.Get<EntLibCacheManager>(name)); }


        public EntLibCacheManagersAdapter(Components components)
        {
            Guard.AgainstNull(components, "components");
            this.components = components;
        }
    }


    public interface Logger
    {
        /// <summary>
        /// Writes a new log entry to the default category.
        /// </summary>
        /// <param name="message">message to log; value from ToString() method from message object.</param>
        void Log(object message);
        
        /// <summary>
        /// Writes a new log entry to the default category with a dictionary of extended properties
        /// </summary>
        /// <param name="message">message to log; value from ToString() method from message object.</param>
        /// <param name="properties">Dictionary of key/value pairs to log.</param>
        void Log(object message, IDictionary<string, object> properties);
        
        /// <summary>
        /// Writes a new log entry to a specific collection of categories.
        /// </summary>
        /// <param name="message">message to log; value from ToString() method from message object.</param>
        /// <param name="categories">Category names used to route the log entry to a one or more trace listeners.</param>
        void Log(object message, IEnumerable<string> categories);
        
        /// <summary>
        /// Writes a new log entry to a specific category.
        /// </summary>
        /// <param name="message">message to log; value from ToString() method from message object.</param>
        /// <param name="category">Category name used to route the log entry to a one or more trace listeners.</param>
        void Log(object message, string category);

        /// <summary>
        /// Write a new log entry to a specific collection of categories with a dictionary of extended properties.
        /// </summary>
        /// <param name="message">message to log; value from ToString() method from message object.</param>
        /// <param name="categories">Category names used to route the log entry to a one or more trace listeners.</param>
        /// <param name="properties">Dictionary of key/value pairs to log.</param>
        void Log(object message, IEnumerable<string> categories, IDictionary<string, object> properties);
        
        /// <summary>
        /// Write a new log entry with a specific collection of categories and priority.
        /// </summary>
        /// <param name="message">Message body to log. Value from ToString() method from message object.</param>
        /// <param name="categories">Category names used to route the log entry to a one or more trace listeners.</param>
        /// <param name="priority">Only messages must be above the minimum priority are processed.</param>
        void Log(object message, IEnumerable<string> categories, int priority);

        /// <summary>
        /// Write a new log entry to a specific category with a dictionary of extended properties.
        /// </summary>
        /// <param name="message">Message body to log. Value from ToString() method from message object.</param>
        /// <param name="category">Category name used to route the log entry to a one or more trace listeners.</param>
        /// <param name="properties">Dictionary of key/value pairs to log.</param>
        void Log(object message, string category, IDictionary<string, object> properties);

        /// <summary>
        /// Write a new log entry with a specific category and priority.
        /// </summary>
        /// <param name="message">Message body to log. Value from ToString() method from message object.</param>
        /// <param name="category">Category name used to route the log entry to a one or more trace listeners.</param>
        /// <param name="priority">Only messages must be above the minimum priority are processed.</param>
        void Log(object message, string category, int priority);

        /// <summary>
        /// Write a new log entry to with a specific collection of categories, priority and a dictionary of extended properties.
        /// </summary>
        /// <param name="message">Message body to log. Value from ToString() method from message object.</param>
        /// <param name="categories">Category names used to route the log entry to a one or more trace listeners.</param>
        /// <param name="priority">Only messages must be above the minimum priority are processed.</param>
        /// <param name="properties">Dictionary of key/value pairs to log.</param>
        void Log(object message, IEnumerable<string> categories, int priority, IDictionary<string, object> properties);

        /// <summary>
        /// Write a new log entry with a specific collection of categories, priority and event id.
        /// </summary>
        /// <param name="message">Message body to log. Value from ToString() method from message object.</param>
        /// <param name="categories">Category names used to route the log entry to a one or more trace listeners.</param>
        /// <param name="priority">Only messages must be above the minimum priority are processed.</param>
        /// <param name="eventId">Event number or identifier.</param>
        void Log(object message, IEnumerable<string> categories, int priority, int eventId);

        /// <summary>
        /// Write a new log entry to with a specific category, priority and a dictionary of extended properties.
        /// </summary>
        /// <param name="message">Message body to log. Value from ToString() method from message object.</param>
        /// <param name="category">Category name used to route the log entry to a one or more trace listeners.</param>
        /// <param name="priority">Only messages must be above the minimum priority are processed.</param>
        /// <param name="properties">Dictionary of key/value pairs to log.</param>
        void Log(object message, string category, int priority, IDictionary<string, object> properties);

        /// <summary>
        /// Write a new log entry with a specific category, priority and event id.
        /// </summary>
        /// <param name="message">Message body to log. Value from ToString() method from message object.</param>
        /// <param name="category">Category name used to route the log entry to a one or more trace listeners.</param>
        /// <param name="priority">Only messages must be above the minimum priority are processed.</param>
        /// <param name="eventId">Event number or identifier.</param>
        void Log(object message, string category, int priority, int eventId);

        /// <summary>
        /// Write a new log entry with a specific collection of categories, priority, event id and severity.
        /// </summary>
        /// <param name="message">Message body to log. Value from ToString() method from message object.</param>
        /// <param name="categories">Category names used to route the log entry to a one or more trace listeners.</param>
        /// <param name="priority">Only messages must be above the minimum priority are processed.</param>
        /// <param name="eventId">Event number or identifier.</param>
        /// <param name="severity">Log entry severity as a System.Diagnostics.TraceEventType enumeration. (Unspecified, Information, Warning or Error).</param>
        void Log(object message, IEnumerable<string> categories, int priority, int eventId, TraceEventType severity);

        /// <summary>
        /// Write a new log entry with a specific category, priority, event id and severity.
        /// </summary>
        /// <param name="message">Message body to log. Value from ToString() method from message object.</param>
        /// <param name="category">Category name used to route the log entry to a one or more trace listeners.</param>
        /// <param name="priority">Only messages must be above the minimum priority are processed.</param>
        /// <param name="eventId">Event number or identifier.</param>
        /// <param name="severity">Log entry severity as a System.Diagnostics.TraceEventType enumeration. (Unspecified, Information, Warning or Error).</param>
        void Log(object message, string category, int priority, int eventId, TraceEventType severity);

        /// <summary>
        /// Write a new log entry with a specific collection of categories, priority, event id, severity and title.
        /// </summary>
        /// <param name="message">Message body to log. Value from ToString() method from message object.</param>
        /// <param name="categories">Category names used to route the log entry to a one or more trace listeners.</param>
        /// <param name="priority">Only messages must be above the minimum priority are processed.</param>
        /// <param name="eventId">Event number or identifier.</param>
        /// <param name="severity">Log message severity as a System.Diagnostics.TraceEventType enumeration. (Unspecified, Information, Warning or Error).</param>
        /// <param name="title">Additional description of the log entry message</param>
        void Log(object message, IEnumerable<string> categories, int priority, int eventId, TraceEventType severity, string title);

        /// <summary>
        /// Write a new log entry with a specific category, priority, event id, severity and title.
        /// </summary>
        /// <param name="message">Message body to log. Value from ToString() method from message object.</param>
        /// <param name="category">Category name used to route the log entry to a one or more trace listeners.</param>
        /// <param name="priority">Only messages must be above the minimum priority are processed.</param>
        /// <param name="eventId">Event number or identifier.</param>
        /// <param name="severity">Log message severity as a System.Diagnostics.TraceEventType enumeration. (Unspecified, Information, Warning or Error).</param>
        /// <param name="title">Additional description of the log entry message</param>
        void Log(object message, string category, int priority, int eventId, TraceEventType severity, string title);

        /// <summary>
        /// Write a new log entry with a specific category, priority, event Id, severity title and dictionary of extended properties.
        /// </summary>
        /// <param name="message">Message body to log. Value from ToString() method from message object.</param>
        /// <param name="categories">Category names used to route the log entry to a one or more trace listeners.</param>
        /// <param name="priority">Only messages must be above the minimum priority are processed.</param>
        /// <param name="eventId">Event number or identifier.</param>
        /// <param name="severity">Log message severity as a System.Diagnostics.TraceEventType enumeration. (Unspecified, Information, Warning or Error).</param>
        /// <param name="title">Additional description of the log entry message.</param>
        /// <param name="properties">Dictionary of key/value pairs to log.</param>
        void Log(object message, IEnumerable<string> categories, int priority, int eventId, TraceEventType severity, string title, IDictionary<string, object> properties);

        /// <summary>
        /// Write a new log entry with a specific category, priority, event Id, severity title and dictionary of extended properties.
        /// </summary>
        /// <param name="message">Message body to log. Value from ToString() method from message object.</param>
        /// <param name="category">Category name used to route the log entry to a one or more trace listeners.</param>
        /// <param name="priority">Only messages must be above the minimum priority are processed.</param>
        /// <param name="eventId">Event number or identifier.</param>
        /// <param name="severity">Log message severity as a System.Diagnostics.TraceEventType enumeration. (Unspecified, Information, Warning or Error).</param>
        /// <param name="title">Additional description of the log entry message.</param>
        /// <param name="properties">Dictionary of key/value pairs to log.</param>
        void Log(object message, string category, int priority, int eventId, TraceEventType severity, string title, IDictionary<string, object> properties);
    }


    internal class EntLibLogWriterAdapter : Logger
    {
        private readonly LogWriter logWriter = null;


        public void Log(object message)
        { logWriter.Write(message); }

        public void Log(object message, IDictionary<string, object> properties)
        { logWriter.Write(message, properties); }

        public void Log(object message, IEnumerable<string> categories)
        { logWriter.Write(message, categories); }

        public void Log(object message, string category)
        { logWriter.Write(message, category); }

        public void Log(object message, IEnumerable<string> categories, IDictionary<string, object> properties)
        { logWriter.Write(message, categories, properties); }

        public void Log(object message, IEnumerable<string> categories, int priority)
        { logWriter.Write(message, categories, priority); }

        public void Log(object message, string category, IDictionary<string, object> properties)
        { logWriter.Write(message, category, properties); }

        public void Log(object message, string category, int priority)
        { logWriter.Write(message, category, priority); }

        public void Log(object message, IEnumerable<string> categories, int priority, IDictionary<string, object> properties)
        { logWriter.Write(message, categories, priority, properties); }

        public void Log(object message, IEnumerable<string> categories, int priority, int eventId)
        { logWriter.Write(message, categories, priority, eventId); }

        public void Log(object message, string category, int priority, IDictionary<string, object> properties)
        { logWriter.Write(message, category, priority, properties); }

        public void Log(object message, string category, int priority, int eventId)
        { logWriter.Write(message, category, priority, eventId); }

        public void Log(object message, IEnumerable<string> categories, int priority, int eventId, TraceEventType severity)
        { logWriter.Write(message, categories, priority, eventId, severity); }

        public void Log(object message, string category, int priority, int eventId, TraceEventType severity)
        { logWriter.Write(message, category, priority, eventId, severity); }

        public void Log(object message, IEnumerable<string> categories, int priority, int eventId, TraceEventType severity, string title)
        { logWriter.Write(message, categories, priority, eventId, severity, title); }

        public void Log(object message, string category, int priority, int eventId, TraceEventType severity, string title)
        { logWriter.Write(message, category, priority, eventId, severity, title); }

        public void Log(object message, IEnumerable<string> categories, int priority, int eventId, TraceEventType severity, string title, IDictionary<string, object> properties)
        { logWriter.Write(message, categories, priority, eventId, severity, title, properties); }

        public void Log(object message, string category, int priority, int eventId, TraceEventType severity, string title, IDictionary<string, object> properties)
        { logWriter.Write(message, category, priority, eventId, severity, title, properties); }


        public EntLibLogWriterAdapter(LogWriter logWriter)
        {
            Guard.AgainstNull(logWriter, "logWriter");
            this.logWriter = logWriter;
        }
    }


    [ConfigurationElementType(typeof(CustomTraceListenerData))]
    public class DebugTraceListener : CustomTraceListener
    {
        public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, object data)
        {
            if ((data is LogEntry) &amp;&amp; (Formatter != null))
                WriteLine(Formatter.Format((LogEntry)data));
            else
                WriteLine(data.ToString());
        }


        public override void Write(string message)
        { Debug.Write(message); }

        public override void WriteLine(string message)
        { Debug.WriteLine(message); }
    }


    public interface Authorizer
    {
        bool IsAuthorizedTo(IPrincipal user, string context);
    }

    
    public abstract class PrincipalToken
    { }


    public interface PrincipalCache
    {
        IPrincipal GetPrincipal(PrincipalToken token);
        PrincipalToken SavePrincipal(IPrincipal principal);
        void SavePrincipal(IPrincipal principal, PrincipalToken token);
        void ExpirePrincipal(PrincipalToken token);
    }

    
    internal class EntLibPrincipalToken : PrincipalToken
    {
        internal IToken Token
        { get; private set; }

        internal EntLibPrincipalToken(IToken token)
        { Token = token; }
    }


    sealed internal class EntLibAuthorizationRuleProviderAdapter : Authorizer
    {
        private readonly IAuthorizationProvider authorizationProvider = null;
        private readonly ExceptionManager exceptionManager = null;


        public bool IsAuthorizedTo(IPrincipal user, string context)
        { 
            var isAuthorizedOrNot = Settings.Default.UserShouldBeAuthorizedForUnconfiguredAuthorizationContext;

            exceptionManager.Process(
                () => isAuthorizedOrNot = authorizationProvider.Authorize(user, context),
                ExceptionPolicy.AuthorizingUserInContext
            );

            return isAuthorizedOrNot;
        }


        public EntLibAuthorizationRuleProviderAdapter(IAuthorizationProvider authorizationProvider, ExceptionManager exceptionManager)
        {
            Guard.AgainstNull(authorizationProvider, "authorizationProvider");
            Guard.AgainstNull(exceptionManager, "exceptionManager");

            this.authorizationProvider = authorizationProvider;
            this.exceptionManager = exceptionManager;
        }
    }


    sealed internal class EntLibSecurityCacheProviderAdapter : PrincipalCache
    {
        private ISecurityCacheProvider securityCacheProvider = null;

        
        public IPrincipal GetPrincipal(PrincipalToken token)
        { return securityCacheProvider.GetPrincipal(((EntLibPrincipalToken)token).Token); }

        public PrincipalToken SavePrincipal(IPrincipal principal)
        { return new EntLibPrincipalToken(securityCacheProvider.SavePrincipal(principal)); }

        public void SavePrincipal(IPrincipal principal, PrincipalToken token)
        { securityCacheProvider.SavePrincipal(principal, ((EntLibPrincipalToken)token).Token); }

        public void ExpirePrincipal(PrincipalToken token)
        { securityCacheProvider.ExpirePrincipal(((EntLibPrincipalToken)token).Token); }


        public EntLibSecurityCacheProviderAdapter(ISecurityCacheProvider securityCacheProvider)
        { this.securityCacheProvider = securityCacheProvider; }
    }


    public interface Cryptographer
    {
        /// <summary>
        /// Compares plain text input with a computed hash using the given hash provider instance.
        /// </summary>
        /// <remarks>
        /// Use this method to compare hash values. Since hashes contain a random "salt" value, two seperately generated
        /// hashes of the same plain text will result in different values. 
        /// </remarks>
        /// <param name="hashInstance">A hash instance from configuration.</param>
        /// <param name="plaintext">The input as a string for which you want to compare the hash to.</param>
        /// <param name="hashedText">The hash as a string for which you want to compare the input to.</param>
        /// <returns><c>true</c> if plainText hashed is equal to the hashedText. Otherwise, <c>false</c>.</returns>
         bool CompareHash(string hashInstance, string plaintext, string hashedText);

        /// <overrides>
        /// Compares plain text input with a computed hash using the given hash provider instance.
        /// </overrides>
        /// <summary>
        /// Compares plain text input with a computed hash using the given hash provider instance.
        /// </summary>
        /// <remarks>
        /// Use this method to compare hash values. Since hashes may contain a random "salt" value, two seperately generated
        /// hashes of the same plain text may result in different values. 
        /// </remarks>
        /// <param name="hashInstance">A hash instance from configuration.</param>
        /// <param name="plaintext">The input for which you want to compare the hash to.</param>
        /// <param name="hashedText">The hash value for which you want to compare the input to.</param>
        /// <returns><c>true</c> if plainText hashed is equal to the hashedText. Otherwise, <c>false</c>.</returns>
         bool CompareHash(string hashInstance, byte[] plaintext, byte[] hashedText);

        /// <overrides>
        /// Computes the hash value of plain text using the given hash provider instance
        /// </overrides>
        /// <summary>
        /// Computes the hash value of plain text using the given hash provider instance
        /// </summary>
        /// <param name="hashInstance">A hash instance from configuration.</param>
        /// <param name="plaintext">The input for which to compute the hash.</param>
        /// <returns>The computed hash code.</returns>
         byte[] CreateHash(string hashInstance, byte[] plaintext);

        /// <summary>
        /// Computes the hash value of plain text using the given hash provider instance
        /// </summary>
        /// <param name="hashInstance">A hash instance from configuration.</param>
        /// <param name="plaintext">The input for which to compute the hash.</param>
        /// <returns>The computed hash code.</returns>
         string CreateHash(string hashInstance, string plaintext);

        /// <summary>
        /// Decrypts a cipher text using a specified symmetric cryptography provider.
        /// </summary>
        /// <param name="symmetricInstance">A symmetric instance from configuration.</param>
        /// <param name="ciphertext">The cipher text for which you want to decrypt.</param>
        /// <returns>The resulting plain text.</returns>
         byte[] DecryptSymmetric(string symmetricInstance, byte[] ciphertext);

        /// <summary>
        /// Decrypts a cipher text using a specified symmetric cryptography provider.
        /// </summary>
        /// <param name="symmetricInstance">A symmetric instance from configuration.</param>
        /// <param name="ciphertextBase64">The cipher text as a base64 encoded string for which you want to decrypt.</param>
        /// <returns>The resulting plain text as a string.</returns>
         string DecryptSymmetric(string symmetricInstance, string ciphertextBase64);


        /// <summary>
        /// Encrypts a secret using a specified symmetric cryptography provider.
        /// </summary>
        /// <param name="symmetricInstance">A symmetric instance from configuration.</param>
        /// <param name="plaintext">The input for which you want to encrypt.</param>
        /// <returns>The resulting cipher text.</returns>
         byte[] EncryptSymmetric(string symmetricInstance, byte[] plaintext);

        /// <summary>
        /// Encrypts a secret using a specified symmetric cryptography provider.
        /// </summary>
        /// <param name="symmetricInstance">A symmetric instance from configuration.</param>
        /// <param name="plaintext">The input as a base64 encoded string for which you want to encrypt.</param>
        /// <returns>The resulting cipher text as a base64 encoded string.</returns>
         string EncryptSymmetric(string symmetricInstance, string plaintext);
    }


    sealed internal class EntLibCryptographyManagerAdapter : Cryptographer
    {
        private readonly CryptographyManager cryptographyManager = null;


        public bool CompareHash(string hashInstance, string plaintext, string hashedText)
        { return cryptographyManager.CompareHash(hashInstance, plaintext, hashedText); }

        public bool CompareHash(string hashInstance, byte[] plaintext, byte[] hashedText)
        { return cryptographyManager.CompareHash(hashInstance, plaintext, hashedText); }

        public byte[] CreateHash(string hashInstance, byte[] plaintext)
        { return cryptographyManager.CreateHash(hashInstance, plaintext); }

        public string CreateHash(string hashInstance, string plaintext)
        { return cryptographyManager.CreateHash(hashInstance, plaintext); }

        public byte[] DecryptSymmetric(string symmetricInstance, byte[] ciphertext)
        { return cryptographyManager.DecryptSymmetric(symmetricInstance, ciphertext); }

        public string DecryptSymmetric(string symmetricInstance, string ciphertextBase64)
        { return cryptographyManager.DecryptSymmetric(symmetricInstance, ciphertextBase64); }

        public byte[] EncryptSymmetric(string symmetricInstance, byte[] plaintext)
        { return cryptographyManager.EncryptSymmetric(symmetricInstance, plaintext); }

        public string EncryptSymmetric(string symmetricInstance, string plaintext)
        { return cryptographyManager.EncryptSymmetric(symmetricInstance, plaintext); }


        public EntLibCryptographyManagerAdapter(CryptographyManager cryptographyManager)
        { this.cryptographyManager = cryptographyManager; }
    }


    [ConfigurationElementType(typeof(CustomSymmetricCryptoProviderData))]
    public class GeneralPurposeSymmetricCryptoProvider : ISymmetricCryptoProvider
    {
        private const string key = "!@#$FS3423fwwrt2";
        private static readonly byte[] keyAsBytes = UTF8Encoding.UTF8.GetBytes(key);

        private readonly AesManaged cryptographer = new AesManaged()
        {
            Key = keyAsBytes,
            IV = keyAsBytes
        };


        public byte[] Decrypt(byte[] ciphertext)
        {
            using (var memoryStream = new MemoryStream())
            {
                using (var cryptoStream = new CryptoStream(memoryStream, cryptographer.CreateDecryptor(), CryptoStreamMode.Write))
                {
                    cryptoStream.Write(ciphertext, 0, ciphertext.Length);
                    cryptoStream.Flush();
                    cryptoStream.Close();
                    return memoryStream.ToArray();
                }
            }
        }

        public byte[] Encrypt(byte[] plaintext)
        {
            using (var memoryStream = new MemoryStream())
            {
                using (var cryptoStream = new CryptoStream(memoryStream, cryptographer.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    cryptoStream.Write(plaintext, 0, plaintext.Length);
                    cryptoStream.FlushFinalBlock();
                    cryptoStream.Close();
                    return memoryStream.ToArray();
                }
            }
        }


        public GeneralPurposeSymmetricCryptoProvider(NameValueCollection ignored)
     { }
    }
    

    sealed public class QueryResultAccessor : ScopeLimitedObject, IEnumerable<QueryResultAccessor.DataRecord>
    {
        sealed public class DataField
        {
            public string Name
            { get; private set; }

            public object Value
            { get; private set; }


            internal DataField(string name, object value)
            {
                Name = name;
                Value = value;
            }
        }


        sealed public class DataRecord : IndexableByName<object>, IEnumerable<DataField>
        {
            private readonly IDictionary<string, DataField> fields = null;


            public bool TryGet(string name, out object entry)
            {
                var field = (DataField)null;
                var couldOrNot = fields.TryGetValue(name, out field);
                entry = couldOrNot ? field.Value : null;
                return couldOrNot;
            }

            public object this[string name]
            { get { return fields[name].Value; } }

            public IEnumerator<DataField> GetEnumerator()
            { return fields.Values.GetEnumerator(); }

            IEnumerator IEnumerable.GetEnumerator()
            { return GetEnumerator(); }

            IEnumerator<object> IEnumerable<object>.GetEnumerator()
            {
                foreach (var dataField in this)
                    yield return dataField.Value;
            }


            internal DataRecord(IDictionary<int, string> fieldNamesByNum, Func<int, object> getFieldValue)
            {
                var sortedFields = new SortedDictionary<string, DataField>();

                var fieldName = (string)null;
                var numFields = fieldNamesByNum.Count; // separated out for performance
                for (var i = 0; i < numFields; i++)
                {
                    fieldName = fieldNamesByNum[i];
                    sortedFields[fieldName] = new DataField(fieldName, getFieldValue(i));
                }

                this.fields = sortedFields;
            }
        }


        private class DataRecordEnumerator : ScopeLimitedObject, IEnumerator<DataRecord>
        {
            private const int InitialIndex = -1;

            private Func<int, DataRecord> tryGetRecord = null;
            private IDictionary<int, string> fieldNamesByNum = null;

            private int currentIndex = InitialIndex;
            

            public DataRecord Current
            { get; private set; }

            object IEnumerator.Current
            { get { return Current; } }


            protected override void OnDisposeExplicit()
            {
                base.OnDisposeExplicit();
                tryGetRecord = null;
                fieldNamesByNum = null;
            }
            
            public bool MoveNext()
            { return (Current = tryGetRecord(++currentIndex)) != null; }

            public void Reset()
            { currentIndex = InitialIndex; }


            public DataRecordEnumerator(Func<int, DataRecord> tryGetRecord, IDictionary<int, string> fieldNamesByNum)
            {
                Guard.AgainstNull(tryGetRecord, "tryGetRecord");
                Guard.AgainstNull(fieldNamesByNum, "fieldNamesByNum");
                this.tryGetRecord = tryGetRecord;
                this.fieldNamesByNum = fieldNamesByNum;
            }
        }


        private const int InitialIndex = -1;

        private int maxIndexRead = InitialIndex;
        private IList<DataRecord> readRecords = new List<DataRecord>();
        private IDictionary<int, string> fieldNamesByNum = new SortedDictionary<int, string>();
        private IDataReader reader = null;


        protected override void OnDisposeExplicit()
        {
            base.OnDisposeExplicit();
            readRecords = null;
            fieldNamesByNum = null;
            reader.Dispose();
            reader = null;
        }


        public IEnumerator<DataRecord> GetEnumerator()
        { return new DataRecordEnumerator(TryGetRecord, fieldNamesByNum); }

        IEnumerator IEnumerable.GetEnumerator()
        { return GetEnumerator(); }


        private object GetFieldValue(int fieldNum)
        { return reader[fieldNum]; }

        private DataRecord TryGetRecord(int index)
        {
            var record = (DataRecord)null;

            if (index <= maxIndexRead)
                record = readRecords[index];
            else
            {
                if (reader.Read())
                    readRecords.Insert(maxIndexRead = index, record = new DataRecord(fieldNamesByNum, GetFieldValue));
            }

            return record;
        }


        public QueryResultAccessor(IDataReader reader)
        {
            Guard.AgainstNull(reader, "reader");
            Guard.Against(reader, r => r.IsClosed, Resources.CannotUtilizeClosedIDataReader, "reader");

            this.reader = reader;

            var fieldCount = reader.FieldCount; // separated out for performance reasons
            for (var i = 0; i < fieldCount; i++)
                fieldNamesByNum[i] = reader.GetName(i);
        }
    }


    public interface Database
    {
        //
        // Summary:
        //     Does this Database object support asynchronous execution?
        bool SupportsAsync { get; }

        //
        // Summary:
        //     Initiates the asynchronous execution of the storedProcedureName using the
        //     given parameterValues which will return the number of rows affected.
        //
        // Parameters:
        //   storedProcedureName:
        //     The name of the stored procedure to execute.
        //
        //   parameterValues:
        //     An array of paramters to pass to the stored procedure. The parameter values
        //     must be in call order as they appear in the stored procedure.
        //
        //   callback:
        //     The async callback to execute when the result of the operation is available.
        //     Pass null if you don't want to use a callback.
        //
        //   state:
        //     Additional state object to pass to the callback.
        //
        // Returns:
        //     An System.IAsyncResult that can be used to poll or wait for results, or both;
        //     this value is also needed when invoking Microsoft.Practices.EnterpriseLibrary.Data.Database.EndExecuteNonQuery(System.IAsyncResult),
        //     which returns the number of affected records.
        IAsyncResult BeginExecuteNonQuery(string storedProcedureName, AsyncCallback callback, object state, params object[] parameterValues);
        //
        // Summary:
        //     Initiates the asynchronous execution of the storedProcedureName using the
        //     given parameterValues inside a transaction which will return the number of
        //     rows affected.
        //
        // Parameters:
        //   storedProcedureName:
        //     The name of the stored procedure to execute.
        //
        //   transaction:
        //     The System.Data.Common.DbTransaction to execute the command within.
        //
        //   callback:
        //     The async callback to execute when the result of the operation is available.
        //     Pass null if you don't want to use a callback.
        //
        //   state:
        //     Additional state object to pass to the callback.
        //
        //   parameterValues:
        //     An array of paramters to pass to the stored procedure. The parameter values
        //     must be in call order as they appear in the stored procedure.
        //
        // Returns:
        //     An System.IAsyncResult that can be used to poll or wait for results, or both;
        //     this value is also needed when invoking Microsoft.Practices.EnterpriseLibrary.Data.Database.EndExecuteNonQuery(System.IAsyncResult),
        //     which returns the number of affected records.
        IAsyncResult BeginExecuteNonQuery(DbTransaction transaction, string storedProcedureName, AsyncCallback callback, object state, params object[] parameterValues);
        //
        // Summary:
        //     Initiates the asynchronous execution of storedProcedureName using the given
        //     parameterValues which will return a System.Data.IDataReader.
        //
        // Parameters:
        //   storedProcedureName:
        //     The name of the stored procedure to execute.
        //
        //   callback:
        //     The async callback to execute when the result of the operation is available.
        //     Pass null if you don't want to use a callback.
        //
        //   state:
        //     Additional state object to pass to the callback.
        //
        //   parameterValues:
        //     An array of parameters to pass to the stored procedure. The parameter values
        //     must be in call order as they appear in the stored procedure.
        //
        // Returns:
        //     An System.IAsyncResult that can be used to poll or wait for results, or both;
        //     this value is also needed when invoking Microsoft.Practices.EnterpriseLibrary.Data.Database.EndExecuteReader(System.IAsyncResult),
        //     which returns the System.Data.IDataReader.
        IAsyncResult BeginExecuteReader(string storedProcedureName, AsyncCallback callback, object state, params object[] parameterValues);
        //
        // Summary:
        //     Initiates the asynchronous execution of storedProcedureName using the given
        //     parameterValues inside a transaction which will return a System.Data.IDataReader.
        //
        // Parameters:
        //   transaction:
        //     The System.Data.Common.DbTransaction to execute the command within.
        //
        //   storedProcedureName:
        //     The name of the stored procedure to execute.
        //
        //   callback:
        //     The async callback to execute when the result of the operation is available.
        //     Pass null if you don't want to use a callback.
        //
        //   state:
        //     Additional state object to pass to the callback.
        //
        //   parameterValues:
        //     An array of parameters to pass to the stored procedure. The parameter values
        //     must be in call order as they appear in the stored procedure.
        //
        // Returns:
        //     An System.IAsyncResult that can be used to poll or wait for results, or both;
        //     this value is also needed when invoking Microsoft.Practices.EnterpriseLibrary.Data.Database.EndExecuteReader(System.IAsyncResult),
        //     which returns the System.Data.IDataReader.
        IAsyncResult BeginExecuteReader(DbTransaction transaction, string storedProcedureName, AsyncCallback callback, object state, params object[] parameterValues);
        //
        // Summary:
        //     Initiates the asynchronous execution of storedProcedureName using the given
        //     parameterValues which will return a query result accessor.
        //
        // Parameters:
        //   storedProcedureName:
        //     The name of the stored procedure to execute.
        //
        //   callback:
        //     The async callback to execute when the result of the operation is available.
        //     Pass null if you don't want to use a callback.
        //
        //   state:
        //     Additional state object to pass to the callback.
        //
        //   parameterValues:
        //     An array of parameters to pass to the stored procedure. The parameter values
        //     must be in call order as they appear in the stored procedure.
        //
        // Returns:
        //     An System.IAsyncResult that can be used to poll or wait for results, or both;
        //     this value is also needed when invoking Microsoft.Practices.EnterpriseLibrary.Data.Database.EndExecuteQuery(System.IAsyncResult),
        //     which returns the query result accessor.
        IAsyncResult BeginExecuteQuery(string storedProcedureName, AsyncCallback callback, object state, params object[] parameterValues);
        //
        // Summary:
        //     Initiates the asynchronous execution of storedProcedureName using the given
        //     parameterValues inside a transaction which will return a query result accessor.
        //
        // Parameters:
        //   transaction:
        //     The System.Data.Common.DbTransaction to execute the command within.
        //
        //   storedProcedureName:
        //     The name of the stored procedure to execute.
        //
        //   callback:
        //     The async callback to execute when the result of the operation is available.
        //     Pass null if you don't want to use a callback.
        //
        //   state:
        //     Additional state object to pass to the callback.
        //
        //   parameterValues:
        //     An array of parameters to pass to the stored procedure. The parameter values
        //     must be in call order as they appear in the stored procedure.
        //
        // Returns:
        //     An System.IAsyncResult that can be used to poll or wait for results, or both;
        //     this value is also needed when invoking Microsoft.Practices.EnterpriseLibrary.Data.Database.EndExecuteQuery(System.IAsyncResult),
        //     which returns the query result accessor.
        IAsyncResult BeginExecuteQuery(DbTransaction transaction, string storedProcedureName, AsyncCallback callback, object state, params object[] parameterValues);
        //
        // Summary:
        //     Initiates the asynchronous execution of storedProcedureName using the given
        //     parameterValues which will return a single value.
        //
        // Parameters:
        //   storedProcedureName:
        //     The name of the stored procedure to execute.
        //
        //   callback:
        //     The async callback to execute when the result of the operation is available.
        //     Pass null if you don't want to use a callback.
        //
        //   state:
        //     Additional state object to pass to the callback.
        //
        //   parameterValues:
        //     An array of parameters to pass to the stored procedure. The parameter values
        //     must be in call order as they appear in the stored procedure.
        //
        // Returns:
        //     An System.IAsyncResult that can be used to poll or wait for results, or both;
        //     this value is also needed when invoking Microsoft.Practices.EnterpriseLibrary.Data.Database.EndExecuteScalar(System.IAsyncResult),
        //     which returns the actual result.
        IAsyncResult BeginExecuteScalar(string storedProcedureName, AsyncCallback callback, object state, params object[] parameterValues);
        //
        // Summary:
        //     Initiates the asynchronous execution of storedProcedureName using the given
        //     parameterValues inside a transaction which will return a single value.
        //
        // Parameters:
        //   transaction:
        //     The System.Data.Common.DbTransaction to execute the command within.
        //
        //   storedProcedureName:
        //     The name of the stored procedure to execute.
        //
        //   callback:
        //     The async callback to execute when the result of the operation is available.
        //     Pass null if you don't want to use a callback.
        //
        //   state:
        //     Additional state object to pass to the callback.
        //
        //   parameterValues:
        //     An array of parameters to pass to the stored procedure. The parameter values
        //     must be in call order as they appear in the stored procedure.
        //
        // Returns:
        //     An System.IAsyncResult that can be used to poll or wait for results, or both;
        //     this value is also needed when invoking Microsoft.Practices.EnterpriseLibrary.Data.Database.EndExecuteScalar(System.IAsyncResult),
        //     which returns the actual result.
        IAsyncResult BeginExecuteScalar(DbTransaction transaction, string storedProcedureName, AsyncCallback callback, object state, params object[] parameterValues);
        //
        // Summary:
        //     Finishes asynchronous execution of a SQL statement, returning the number
        //     of affected records.
        //
        // Parameters:
        //   asyncResult:
        //     The System.IAsyncResult returned by a call to any overload of Microsoft.Practices.EnterpriseLibrary.Data.Database.BeginExecuteNonQuery(System.Data.Common.DbCommand,System.AsyncCallback,System.Object).
        //
        // Returns:
        //     The number of affected records.
        int EndExecuteNonQuery(IAsyncResult asyncResult);
        //
        // Summary:
        //     Finishes asynchronous execution of a Transact-SQL statement, returning an
        //     System.Data.IDataReader.
        //
        // Parameters:
        //   asyncResult:
        //     The System.IAsyncResult returned by a call to any overload of BeginExecuteReader.
        //
        // Returns:
        //     An System.Data.IDataReader object that can be used to consume the queried
        //     information.
        IDataReader EndExecuteReader(IAsyncResult asyncResult);
        //
        // Summary:
        //     Finishes asynchronous execution of a Transact-SQL statement, returning a
        //     query result accessor.
        //
        // Parameters:
        //   asyncResult:
        //     The System.IAsyncResult returned by a call to any overload of BeginExecuteReader.
        //
        // Returns:
        //     A query result accessor that can be used to consume the queried
        //     information.
        QueryResultAccessor EndExecuteQuery(IAsyncResult asyncResult);
        //
        // Summary:
        //     Finishes asynchronous execution of a Transact-SQL statement, returning the
        //     first column of the first row in the result set returned by the query. Extra
        //     columns or rows are ignored.
        //
        // Parameters:
        //   asyncResult:
        //     The System.IAsyncResult returned by a call to any overload of BeginExecuteScalar.
        //
        // Returns:
        //     The value of the first column of the first row in the result set returned
        //     by the query.  If the result didn't have any columns or rows null (Nothing
        //     in Visual Basic).
        object EndExecuteScalar(IAsyncResult asyncResult);
        //
        // Summary:
        //     Executes the storedProcedureName using the given parameterValues and returns
        //     the number of rows affected.
        //
        // Parameters:
        //   storedProcedureName:
        //     The name of the stored procedure to execute.
        //
        //   parameterValues:
        //     An array of paramters to pass to the stored procedure. The parameter values
        //     must be in call order as they appear in the stored procedure.
        //
        // Returns:
        //     The number of rows affected
        int ExecuteNonQuery(string storedProcedureName, params object[] parameterValues);
        //
        // Summary:
        //     Executes the storedProcedureName using the given parameterValues within a
        //     transaction and returns the number of rows affected.
        //
        // Parameters:
        //   transaction:
        //     The System.Data.IDbTransaction to execute the command within.
        //
        //   storedProcedureName:
        //     The name of the stored procedure to execute.
        //
        //   parameterValues:
        //     An array of parameters to pass to the stored procedure. The parameter values
        //     must be in call order as they appear in the stored procedure.
        //
        // Returns:
        //     The number of rows affected.
        int ExecuteNonQuery(DbTransaction transaction, string storedProcedureName, params object[] parameterValues);
        //
        // Summary:
        //     Executes the storedProcedureName with the given parameterValues and returns
        //     an System.Data.IDataReader through which the result can be read.  It is the
        //     responsibility of the caller to close the connection and reader when finished.
        //
        // Parameters:
        //   storedProcedureName:
        //     The command that contains the query to execute.
        //
        //   parameterValues:
        //     An array of paramters to pass to the stored procedure. The parameter values
        //     must be in call order as they appear in the stored procedure.
        //
        // Returns:
        //     An System.Data.IDataReader object.
        IDataReader ExecuteReader(string storedProcedureName, params object[] parameterValues);
        //
        // Summary:
        //     Executes the storedProcedureName with the given parameterValues within the
        //     given transaction and returns an System.Data.IDataReader through which the
        //     result can be read.  It is the responsibility of the caller to close the
        //     connection and reader when finished.
        //
        // Parameters:
        //   transaction:
        //     The System.Data.IDbTransaction to execute the command within.
        //
        //   storedProcedureName:
        //     The command that contains the query to execute.
        //
        //   parameterValues:
        //     An array of paramters to pass to the stored procedure. The parameter values
        //     must be in call order as they appear in the stored procedure.
        //
        // Returns:
        //     An System.Data.IDataReader object.
        IDataReader ExecuteReader(DbTransaction transaction, string storedProcedureName, params object[] parameterValues);
        //
        // Summary:
        //     Executes the storedProcedureName with the given parameterValues and returns
        //     the first column of the first row in the result set returned by the query.
        //     Extra columns or rows are ignored.
        //
        // Parameters:
        //   storedProcedureName:
        //     The stored procedure to execute.
        //
        //   parameterValues:
        //     An array of paramters to pass to the stored procedure. The parameter values
        //     must be in call order as they appear in the stored procedure.
        //
        // Returns:
        //     The first column of the first row in the result set.
        object ExecuteScalar(string storedProcedureName, params object[] parameterValues);
        //
        // Summary:
        //     Executes the storedProcedureName with the given parameterValues within a
        //     transaction and returns the first column of the first row in the result set
        //     returned by the query. Extra columns or rows are ignored.
        //
        // Parameters:
        //   transaction:
        //     The System.Data.IDbTransaction to execute the command within.
        //
        //   storedProcedureName:
        //     The stored procedure to execute.
        //
        //   parameterValues:
        //     An array of paramters to pass to the stored procedure. The parameter values
        //     must be in call order as they appear in the stored procedure.
        //
        // Returns:
        //     The first column of the first row in the result set.
        object ExecuteScalar(DbTransaction transaction, string storedProcedureName, params object[] parameterValues);
        //
        // Summary:
        //     Executes the storedProcedureName with the given parameterValues and returns
        //     a result accessor through which the result can be read.  It is the
        //     responsibility of the caller to close the accessor when finished (using Dispose).
        //
        // Parameters:
        //   storedProcedureName:
        //     The command that contains the query to execute.
        //
        //   parameterValues:
        //     An array of paramters to pass to the stored procedure. The parameter values
        //     must be in call order as they appear in the stored procedure.
        //
        // Returns:
        //     a query result accessor
        QueryResultAccessor ExecuteQuery(string storedProcedureName, params object[] parameterValues);
        //
        // Summary:
        //     Executes the storedProcedureName with the given parameterValues within the
        //     given transaction and returns a result accessor through which the
        //     result can be read.  It is the responsibility of the caller to close the
        //     accessor when finished (using dispose).
        //
        // Parameters:
        //   transaction:
        //     The System.Data.IDbTransaction to execute the command within.
        //
        //   storedProcedureName:
        //     The command that contains the query to execute.
        //
        //   parameterValues:
        //     An array of paramters to pass to the stored procedure. The parameter values
        //     must be in call order as they appear in the stored procedure.
        //
        // Returns:
        //     a query result accessor
        QueryResultAccessor ExecuteQuery(DbTransaction transaction, string storedProcedureName, params object[] parameterValues);
    }


    public interface Databases
    {
        Database GetDefault();
        Database Get(string name);
    }


    sealed internal class EntLibDatabaseAdapter : Database
    {
        private readonly EntLibDatabase database = null;


        public bool SupportsAsync
        { get { return database.SupportsAsync; } }

        public IAsyncResult BeginExecuteNonQuery(string storedProcedureName, AsyncCallback callback, object state, params object[] parameterValues)
        { return database.BeginExecuteNonQuery(storedProcedureName, callback, state, parameterValues); }

        public IAsyncResult BeginExecuteNonQuery(DbTransaction transaction, string storedProcedureName, AsyncCallback callback, object state, params object[] parameterValues)
        { return database.BeginExecuteNonQuery(transaction, storedProcedureName, callback, state, parameterValues); }

        public IAsyncResult BeginExecuteReader(string storedProcedureName, AsyncCallback callback, object state, params object[] parameterValues)
        { return database.BeginExecuteReader(storedProcedureName, callback, state, parameterValues); }

        public IAsyncResult BeginExecuteReader(DbTransaction transaction, string storedProcedureName, AsyncCallback callback, object state, params object[] parameterValues)
        { return database.BeginExecuteReader(transaction, storedProcedureName, callback, state, parameterValues); }

        public IAsyncResult BeginExecuteScalar(string storedProcedureName, AsyncCallback callback, object state, params object[] parameterValues)
        { return database.BeginExecuteScalar(storedProcedureName, callback, state, parameterValues); }

        public IAsyncResult BeginExecuteScalar(DbTransaction transaction, string storedProcedureName, AsyncCallback callback, object state, params object[] parameterValues)
        { return database.BeginExecuteScalar(transaction, storedProcedureName, callback, state, parameterValues); }

        public IAsyncResult BeginExecuteQuery(string storedProcedureName, AsyncCallback callback, object state, params object[] parameterValues)
        { return BeginExecuteReader(storedProcedureName, callback, state, parameterValues); }

        public IAsyncResult BeginExecuteQuery(DbTransaction transaction, string storedProcedureName, AsyncCallback callback, object state, params object[] parameterValues)
        { return BeginExecuteReader(transaction, storedProcedureName, callback, state, parameterValues); }

        public int EndExecuteNonQuery(IAsyncResult asyncResult)
        { return database.EndExecuteNonQuery(asyncResult); }

        public IDataReader EndExecuteReader(IAsyncResult asyncResult)
        { return database.EndExecuteReader(asyncResult); }

        public QueryResultAccessor EndExecuteQuery(IAsyncResult asyncResult)
        { return new QueryResultAccessor(EndExecuteReader(asyncResult)); }

        public object EndExecuteScalar(IAsyncResult asyncResult)
        { return database.EndExecuteScalar(asyncResult); }

        public int ExecuteNonQuery(string storedProcedureName, params object[] parameterValues)
        { return database.ExecuteNonQuery(storedProcedureName, parameterValues); }

        public int ExecuteNonQuery(DbTransaction transaction, string storedProcedureName, params object[] parameterValues)
        { return database.ExecuteNonQuery(transaction, storedProcedureName, parameterValues); }

        public IDataReader ExecuteReader(string storedProcedureName, params object[] parameterValues)
        { return database.ExecuteReader(storedProcedureName, parameterValues); }

        public IDataReader ExecuteReader(DbTransaction transaction, string storedProcedureName, params object[] parameterValues)
        { return database.ExecuteReader(transaction, storedProcedureName, parameterValues); }

        public QueryResultAccessor ExecuteQuery(string storedProcedureName, params object[] parameterValues)
        { return new QueryResultAccessor(ExecuteReader(storedProcedureName, parameterValues)); }

        public QueryResultAccessor ExecuteQuery(DbTransaction transaction, string storedProcedureName, params object[] parameterValues)
        { return new QueryResultAccessor(ExecuteReader(transaction, storedProcedureName, parameterValues)); }

        public object ExecuteScalar(string storedProcedureName, params object[] parameterValues)
        { return database.ExecuteScalar(storedProcedureName, parameterValues); }

        public object ExecuteScalar(DbTransaction transaction, string storedProcedureName, params object[] parameterValues)
        { return database.ExecuteScalar(transaction, storedProcedureName, parameterValues); }


        public EntLibDatabaseAdapter(EntLibDatabase database)
        {
            Guard.AgainstNull(database, "database");
            this.database = database;
        }
    }


    sealed internal class EntLibDatabasesAdapter : Databases
    {
        private readonly Components components = null;


        public Database GetDefault()
        { return new EntLibDatabaseAdapter(components.Get<EntLibDatabase>()); }

        public Database Get(string name)
        { return new EntLibDatabaseAdapter(components.Get<EntLibDatabase>(name)); }


        public EntLibDatabasesAdapter(Components components)
        {
            Guard.AgainstNull(components, "components");
            this.components = components;
        }
    }