Sunday, June 10, 2012

Utilizing the command pattern to support CQRS on top of a domain framework

Building on top of the domain framework I posted last time, I wanted to share a command pattern implementation I've come up with to try and make enabling CQRS scenarios easier.

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



    public interface Executable
    {
        void Execute();
    }


    public interface Command : Executable
    { }


    public interface Query : Executable
    { }


    public abstract class UnitOfWorkBasedCommand : UnitOfWorkBasedObject, Command
    {
        protected abstract void ExecuteCommand();


        public void Execute()
        { ExecuteCommand(); }


        public UnitOfWorkBasedCommand(UnitOfWork unitOfWork) : base(unitOfWork)
        { }
    }


    public abstract class UnitOfWorkBasedQuery : UnitOfWorkBasedObject, Query
    {
        protected abstract void ExecuteQuery();


        protected bool ShouldExecuteAsReadOnly
        { get; private set; }


        public void Execute()
        { ExecuteQuery(); }
        

        public UnitOfWorkBasedQuery(UnitOfWork unitOfWork, bool shouldExecuteAsReadOnly = true) : base(unitOfWork)
        { ShouldExecuteAsReadOnly = shouldExecuteAsReadOnly; }
    }


    public abstract class RepositoryAccessingCommand : UnitOfWorkBasedCommand
    {
        private readonly RepositoryFactory repositories = null;


        protected Repository<TEntity> GetRepositoryAddedToUnitOfWorkContexts<TEntity>() where TEntity : class, new()
        {
            var repository = repositories.Get<TEntity>();
            UnitOfWork.AddContext(repository);
            return repository;
        }


        protected ReadOnlyRepository<TEntity> GetReadOnlyRepositoryAddedToUnitOfWorkContexts<TEntity>() where TEntity : class, new()
        {
            var repository = repositories.GetReadOnly<TEntity>();
            UnitOfWork.AddContext(repository);
            return repository;
        }


        public RepositoryAccessingCommand(UnitOfWork unitOfWork, RepositoryFactory repositoryFactory) : base(unitOfWork)
        {
            repositories = repositoryFactory;
        }
    }


    public abstract class RepositoryAccessingQuery : UnitOfWorkBasedQuery
    {
        private readonly RepositoryFactory repositories = null;


        protected Repository<TEntity> GetRepositoryAddedToUnitOfWorkContexts<TEntity>() where TEntity : class, new()
        {
            var repository = repositories.Get<TEntity>();
            UnitOfWork.AddContext(repository);
            return repository;
        }


        protected ReadOnlyRepository<TEntity> GetReadOnlyRepositoryAddedToUnitOfWorkContexts<TEntity>() where TEntity : class, new()
        {
            var repository = repositories.GetReadOnly<TEntity>();
            UnitOfWork.AddContext(repository);
            return repository;
        }


        public RepositoryAccessingQuery(
            UnitOfWork unitOfWork, 
            RepositoryFactory repositoryFactory,
            bool shouldExecuteAsReadOnly = true
        ) : base(unitOfWork, shouldExecuteAsReadOnly)
        {
            repositories = repositoryFactory;
        }
    }
    

Sunday, June 3, 2012

Decoupling your application domain from your ORM of choice

Domain-driven design and development (DDD/DDDD) is a very popular approach for making software.  There are a variety of ORM's that make implementation of a domain model easier, such as Entity Framework and Lightspeed.   You want to decouple your application from your chosen ORM, in case you need to switch to a different ORM later. Here is an example domain framework which will enable common domain design patterns like Repository and Unit of Work.   I use Entity Framework a lot so I've included adapters for it too. Note: the ExceptionManager is one that is similar to one I shared in an earlier post.

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

    public interface Committable
    {
        void Commit();
    }

    
    public interface ContextProvider
    {
        object GetContext();
    }


    public interface UnitOfWork : Committable, IDisposable
    {
        UnitOfWork AddContext(object context);
    }


    
    public abstract class UnitOfWorkBase : IDisposable, UnitOfWork
    {
        private readonly ICollection<object> contexts = new Collection<object>();

        protected IEnumerable<object> Contexts
        { get { return contexts; } }


        protected IEnumerable<object> GetUniqueContexts()
        {
            var uniqueContexts = new Collection<object>();

            foreach (var context in Contexts)
            {
                var rootContext = context;
                while (rootContext is ContextProvider) 
                    rootContext = ((ContextProvider)rootContext).GetContext();
                if (!uniqueContexts.Contains(rootContext))
                    uniqueContexts.Add(rootContext);
            }

            return uniqueContexts;
        }


        public abstract void Commit();        


        public UnitOfWork AddContext(object context)
        {
            if (!contexts.Contains(context))
                contexts.Add(context);
            return this;
        }

        
        public void Dispose()
        {
            Commit();
            contexts.Clear();
        }
    }


    public class ContextManagingUnitOfWork : UnitOfWorkBase
    {
        public override void Commit()
        {
            
            foreach (var context in GetUniqueContexts())
            {
                var committable = context as Committable;
                if (committable != null)
                    committable.Commit();
            }
        }

        protected override void OnDisposedExplicitAfterCommit()
        {
            foreach (var context in GetUniqueContexts())
            {
                var disposable = context as IDisposable;
                if (disposable != null)
                    disposable.Dispose();
            }

            base.OnDisposedExplicitAfterCommit();
        }
    }


    public abstract class UnitOfWorkBasedObject
    {
        protected UnitOfWork UnitOfWork
        { get; private set; }

        
        public UnitOfWorkBasedObject(UnitOfWork unitOfWork)
        {
            UnitOfWork = unitOfWork;
        }
    }


    public interface DomainContext : Committable, IDisposable
    {
        ObjectSet<TEntity> GetContextFor<TEntity>() where TEntity : class, new();
        void SetStateOf<TEntity>(TEntity entity, EntityState state) where TEntity : class, new();
        EntityState GetStateOf<TEntity>(TEntity entity) where TEntity : class, new();
        bool TryGetStateOf<TEntity>(TEntity entity, out EntityState state) where TEntity : class, new();

        int ExecuteCommand(string commandText, params object[] parameters);

        ObjectResult<TElement> ExecuteQuery<TElement>(string queryText, params object[] parameters) where TElement : new();
        ObjectResult<TEntity> ExecuteQuery<TEntity>(string queryText, string entitySetName, bool asReadOnly, params object[] parameters) where TEntity : class, new();

        int ExecuteFunction(string functionName, params ObjectParameter[] parameters);
        ObjectResult<TElement> ExecuteFunction<TElement>(string functionName, params ObjectParameter[] parameters) where TElement : new();
        ObjectResult<TEntity> ExecuteFunction<TEntity>(string functionName, bool asReadOnly, params ObjectParameter[] parameters) where TEntity : class, new();

        int ExecuteStoredProcedure(string storedProcedureName, params ObjectParameter[] parameters);
        ObjectResult<TElement> ExecuteStoredProcedure<TElement>(string storedProcedureName, params ObjectParameter[] parameters) where TElement : new();
        ObjectResult<TEntity> ExecuteStoredProcedure<TEntity>(string storedProcedureName, bool asReadOnly, params ObjectParameter[] parameters) where TEntity : class, new();
    }


    public interface Repository<TEntity> where TEntity : class, new()
    {
        IQueryable<TEntity> AsQueryable(bool asReadOnly = false, IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null);

        TEntity Get(IEnumerable<object> keyValues, bool asReadOnly = false, IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null);
        TEntity Create();
        TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, TEntity, new();
        TEntity Add(TEntity entity);
        TEntity Remove(IEnumerable<object> keyValues);
        TEntity Remove(TEntity entity);
        TEntity Update(TEntity entity);

        IEnumerable<TEntity> Where(
            Expression<Func<TEntity, bool>> criteria,
            Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
            bool asReadOnly = false,
            IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null
        );

        IEnumerable<TEntity> GetAll(
            Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
            bool asReadOnly = false,
            IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null
        );
        

        TEntity Single(Expression<Func<TEntity, bool>> criteria = null, bool asReadOnly = false, IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null);
        TEntity SingleOrDefault(Expression<Func<TEntity, bool>> criteria = null, bool asReadOnly = false, IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null);
        TEntity First(Expression<Func<TEntity, bool>> criteria = null, bool asReadOnly = false, IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null);
        TEntity FirstOrDefault(Expression<Func<TEntity, bool>> criteria = null, bool asReadOnly = false, IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null);
    }


    public interface ReadOnlyRepository<TEntity> where TEntity : class, new()
    {
        IQueryable<TEntity> AsQueryable();

        TEntity Get(IEnumerable<object> keyValues, IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null);
        
        IEnumerable<TEntity> Where(
            Expression<Func<TEntity, bool>> criteria,
            Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
            IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null
        );

        IEnumerable<TEntity> GetAll(
            Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
            IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null
        );


        TEntity Single(Expression<Func<TEntity, bool>> criteria = null, IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null);
        TEntity SingleOrDefault(Expression<Func<TEntity, bool>> criteria = null, IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null);
        TEntity First(Expression<Func<TEntity, bool>> criteria = null, IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null);
        TEntity FirstOrDefault(Expression<Func<TEntity, bool>> criteria = null, IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null);
    }


    public interface RepositoryFactory
    {
        Repository<T> Get<T>() where T : class, new();
        ReadOnlyRepository<T> GetReadOnly<T>() where T : class, new();
    }


    public class ReadOnlyRepositoryWrapper<TEntity> : ReadOnlyRepository<TEntity>, ContextProvider where TEntity : class, new()
    {
        private readonly Repository<TEntity> repository = null;


        public IQueryable<TEntity> AsQueryable()
        { return repository.AsQueryable(asReadOnly: true); }

        public TEntity Get(IEnumerable<object> keyValues, IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null)
        { return repository.Get(keyValues, asReadOnly: true, eagerlyLoad: eagerlyLoad); }

        public IEnumerable<TEntity> Where(Expression<Func<TEntity, bool>> criteria, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null)
        { return repository.Where(criteria, orderBy: orderBy, asReadOnly: true, eagerlyLoad: eagerlyLoad); }

        public IEnumerable<TEntity> GetAll(Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null)
        { return repository.GetAll(orderBy: orderBy, asReadOnly: true, eagerlyLoad: eagerlyLoad); }

        public TEntity Single(Expression<Func<TEntity, bool>> criteria, IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null)
        { return repository.Single(criteria: criteria, asReadOnly: true, eagerlyLoad: eagerlyLoad); }

        public TEntity SingleOrDefault(Expression<Func<TEntity, bool>> criteria, IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null)
        { return repository.SingleOrDefault(criteria: criteria, asReadOnly: true, eagerlyLoad: eagerlyLoad); }

        public TEntity First(Expression<Func<TEntity, bool>> criteria, IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null)
        { return repository.First(criteria: criteria, asReadOnly: true, eagerlyLoad: eagerlyLoad); }

        public TEntity FirstOrDefault(Expression<Func<TEntity, bool>> criteria, IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null)
        { return repository.FirstOrDefault(criteria: criteria, asReadOnly: true, eagerlyLoad: eagerlyLoad); }


        object ContextProvider.GetContext()
        { return repository; }


        public ReadOnlyRepositoryWrapper(Repository<TEntity> repository)
        {
            this.repository = repository;
        }
    }


    public class DomainContextBasedRepository<TEntity> : Repository<TEntity>, ContextProvider where TEntity : class, new()
    {
        protected static readonly object KeyPropertiesLock = new object();
        protected static IEnumerable<PropertyInfo> KeyProperties = null;

        protected DomainContext DomainContext
        { get; private set; }
        
        protected ExceptionManager ExceptionManager
        { get; private set; }

        protected string DefaultExceptionPolicy
        { get; set; }


        protected void ProcessWithinExceptionPolicy(Action action, string exceptionPolicy)
        { ExceptionManager.Handle(action, exceptionPolicy); }

        protected TResult ProcessedWithinExceptionPolicy<TResult>(Func<TResult> func, string exceptionPolicy)
        {
            var result = default(TResult);
            ExceptionManager.Handle(() => result = func(), exceptionPolicy);
            return result;
        }


        protected void ProcessWithinExceptionPolicy(Action action)
        { ProcessWithinExceptionPolicy(action, DefaultExceptionPolicy); }

        protected TResult ProcessedWithinExceptionPolicy<TResult>(Func<TResult> func)
        { return ProcessedWithinExceptionPolicy(func, DefaultExceptionPolicy); }
        

        protected Expression<Func<TEntity, bool>> GetKeyComparisonExpression(IEnumerable<object> keyValues)
        {
            var propertyEqualityExpressions = new Queue<Expression>();

            var keyPropertiesEnumerator = KeyProperties.GetEnumerator();
            var keyValuesEnumerator = keyValues.GetEnumerator();

            var entityParam = Expression.Parameter(typeof(TEntity), "entity");

            while (keyPropertiesEnumerator.MoveNext() && keyValuesEnumerator.MoveNext())
                propertyEqualityExpressions.Enqueue(
                    Expression.Equal(
                        Expression.Property(entityParam, keyPropertiesEnumerator.Current),
                        Expression.Constant(keyValuesEnumerator.Current)
                    )
                );

            var expression = propertyEqualityExpressions.Dequeue();
            while (propertyEqualityExpressions.Any())
                expression = Expression.AndAlso(expression, propertyEqualityExpressions.Dequeue());

            return Expression.Lambda<Func<TEntity, bool>>(expression, entityParam);
        }


        
        protected virtual ObjectSet<TEntity> GetObjectSet(bool asReadOnly = false)
        {
            var objectSet = DomainContext.GetContextFor<TEntity>();
            if (asReadOnly)
                objectSet.MergeOption = MergeOption.NoTracking;
            return objectSet;
        }

        protected virtual ObjectQuery<TEntity> GetQuery(bool asReadOnly = false, IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null)
        {
            var query = (ObjectQuery<TEntity>) GetObjectSet(asReadOnly);
            if (eagerlyLoad != null)
                foreach (var propertyToEagerLoad in eagerlyLoad)
                    query = query.Include(propertyToEagerLoad.AsPropertyPath());
            return query;
        }


        protected ObjectQuery<TEntity> GetQuery(Expression<Func<TEntity, bool>> criteria, bool asReadOnly, IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad)
        {
            var query = GetQuery(asReadOnly, eagerlyLoad);
            return (ObjectQuery<TEntity>)(criteria != null ? query.Where(criteria) : query);
        }


        public virtual IQueryable<TEntity> AsQueryable(bool asReadOnly = false, IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null)
        { return ProcessedWithinExceptionPolicy(() => GetQuery(asReadOnly, eagerlyLoad)); }


        protected virtual TEntity Get(ObjectQuery<TEntity> objectSet, IEnumerable<object> keyValues)
        {
            return objectSet.Where(GetKeyComparisonExpression(keyValues)).FirstOrDefault();
        }


        public TEntity Get(IEnumerable<object> keyValues, bool asReadOnly = false, IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null)
        {
            return ProcessedWithinExceptionPolicy(() => Where(GetKeyComparisonExpression(keyValues), asReadOnly: asReadOnly, eagerlyLoad: eagerlyLoad).FirstOrDefault());
        }

        public virtual TEntity Create()
        { return ProcessedWithinExceptionPolicy(() => GetObjectSet().CreateObject()); }

        public virtual TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, TEntity, new()
        { return ProcessedWithinExceptionPolicy(() => GetObjectSet().CreateObject<TDerivedEntity>()); }

        public virtual TEntity Add(TEntity entity)
        {
            return ProcessedWithinExceptionPolicy(() =>
            {
                GetObjectSet().AddObject(entity);
                return entity;
            });
        }

        public virtual TEntity Remove(IEnumerable<object> keyValues)
        {
            return ProcessedWithinExceptionPolicy(() =>
            {
                var objectSet = GetObjectSet();
                var entity = Get(objectSet, keyValues);
                objectSet.DeleteObject(entity);
                return entity;
            });
        }

        public virtual TEntity Remove(TEntity entity)
        {
            return ProcessedWithinExceptionPolicy(() =>
            {
                var objectSet = GetObjectSet();
                if (DomainContext.GetStateOf(entity) == EntityState.Detached)
                    objectSet.Attach(entity);
                objectSet.DeleteObject(entity);
                return entity;
            });
        }

        public virtual TEntity Update(TEntity entity)
        {
            return ProcessedWithinExceptionPolicy(() =>
            {
                try
                {
                    GetObjectSet().Attach(entity);
                    DomainContext.SetStateOf(entity, EntityState.Modified);
                }
                catch (InvalidOperationException)
                { entity = GetObjectSet().ApplyCurrentValues(entity); }

                return entity;
            });
        }
        

        public virtual IEnumerable<TEntity> Where(
            Expression<Func<TEntity, bool>> criteria,
            Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
            bool asReadOnly = false,
            IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null
        )
        {
            return ProcessedWithinExceptionPolicy(() =>
            {
                var query = GetQuery(criteria, asReadOnly, eagerlyLoad);
                return ((orderBy != null) ? orderBy(query) : query).ToArray();
            });
        }


        public virtual IEnumerable<TEntity> GetAll(
            Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
            bool asReadOnly = false,
            IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null
        )
        { return ProcessedWithinExceptionPolicy(() => Where(entity => true, orderBy, asReadOnly, eagerlyLoad)); }


        public virtual TEntity Single(Expression<Func<TEntity, bool>> criteria = null, bool asReadOnly = false, IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null)
        { return ProcessedWithinExceptionPolicy(() => GetQuery(criteria, asReadOnly, eagerlyLoad).Single(criteria)); }


        public virtual TEntity SingleOrDefault(Expression<Func<TEntity, bool>> criteria = null, bool asReadOnly = false, IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null)
        { return ProcessedWithinExceptionPolicy(() => GetQuery(criteria, asReadOnly, eagerlyLoad).SingleOrDefault(criteria)); }


        public virtual TEntity First(Expression<Func<TEntity, bool>> criteria = null, bool asReadOnly = false, IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null)
        { return ProcessedWithinExceptionPolicy(() => GetQuery(criteria, asReadOnly, eagerlyLoad).First(criteria)); }


        public virtual TEntity FirstOrDefault(Expression<Func<TEntity, bool>> criteria = null, bool asReadOnly = false, IEnumerable<Expression<Func<TEntity, object>>> eagerlyLoad = null)
        { return ProcessedWithinExceptionPolicy(() => GetQuery(criteria, asReadOnly, eagerlyLoad).FirstOrDefault(criteria)); }
        

        object ContextProvider.GetContext()
        { return DomainContext; }



        public DomainContextBasedRepository(DomainContext domainContext, ExceptionManager exceptionManager)
        {
            this.DomainContext = domainContext;
            this.ExceptionManager = exceptionManager;
            this.DefaultExceptionPolicy = null;

            if (KeyProperties == null)
            {
                lock (KeyPropertiesLock)
                {
                    if (KeyProperties == null)
                    {
                        var defaultKeyOrder = 0;
                        var keyProperties = GetObjectSet().EntitySet.ElementType.KeyMembers
                            .Select(m => typeof(TEntity).GetProperty(m.Name))
                            .Select(property =>
                            {
                                var columnAttribute = property.GetCustomAttribute<ColumnAttribute>();
                                return new Tuple<int, PropertyInfo>((columnAttribute == null) ? defaultKeyOrder : columnAttribute.Order, property);
                            });


                        KeyProperties = keyProperties.OrderBy(tuple => tuple.Item1).Select(tuple => tuple.Item2);
                    }
                }
            }
        }
    }


    public class DomainContextBasedRepositoryFactory : RepositoryFactory
    {
        private readonly DomainContext domainContext = null;
        private readonly ExceptionManager exceptionManager = null;


        public Repository<T> Get<T>() where T : class, new()
        { return new DomainContextBasedRepository<T>(domainContext, exceptionManager); }

        public ReadOnlyRepository<T> GetReadOnly<T>() where T : class, new()
        { return new ReadOnlyRepositoryWrapper<T>(Get<T>()); }


        public DomainContextBasedRepositoryFactory(DomainContext domainContext, ExceptionManager exceptionManager)
        {
            this.domainContext = domainContext;
            this.exceptionManager = exceptionManager;
        }
    }
    

    public abstract class ObjectContextBasedDomainContext : ScopeLimitedObject, DomainContext
    {
        protected abstract ObjectContext ObjectContext { get; }


        public virtual ObjectSet<TEntity> GetContextFor<TEntity>() where TEntity : class, new()
        { return ObjectContext.CreateObjectSet<TEntity>(); }

        public virtual void SetStateOf<TEntity>(TEntity entity, EntityState state) where TEntity : class, new()
        { ObjectContext.ObjectStateManager.ChangeObjectState(entity, state); }

        public virtual EntityState GetStateOf<TEntity>(TEntity entity) where TEntity : class, new()
        { return ObjectContext.ObjectStateManager.GetObjectStateEntry(entity).State; }

        public bool TryGetStateOf<TEntity>(TEntity entity, out EntityState entityState) where TEntity : class, new()
        {
            var objectStateEntry = (ObjectStateEntry) null;
            var canOrNot = ObjectContext.ObjectStateManager.TryGetObjectStateEntry(entity, out objectStateEntry);
            entityState = canOrNot ? objectStateEntry.State : default(EntityState);
            return canOrNot;
        }
        

        public int ExecuteCommand(string commandText, params object[] parameters)
        { return ObjectContext.ExecuteStoreCommand(commandText, parameters); }


        public ObjectResult<TElement> ExecuteQuery<TElement>(string queryText, params object[] parameters) where TElement : new()
        { return ObjectContext.ExecuteStoreQuery<TElement>(queryText, parameters); }

        public ObjectResult<TEntity> ExecuteQuery<TEntity>(string queryText, string entitySetName, bool asReadOnly, params object[] parameters) where TEntity : class, new()
        { return ObjectContext.ExecuteStoreQuery<TEntity>(queryText, entitySetName, asReadOnly ? MergeOption.NoTracking : MergeOption.AppendOnly, parameters); }


        public int ExecuteFunction(string functionName, params ObjectParameter[] parameters)
        { return ObjectContext.ExecuteFunction(functionName, parameters); }

        public ObjectResult<TElement> ExecuteFunction<TElement>(string functionName, params ObjectParameter[] parameters) where TElement : new()
        { return ObjectContext.ExecuteFunction<TElement>(functionName, parameters); }

        public ObjectResult<TEntity> ExecuteFunction<TEntity>(string functionName, bool asReadOnly, params ObjectParameter[] parameters) where TEntity : class, new()
        { return ObjectContext.ExecuteFunction<TEntity>(functionName, asReadOnly ? MergeOption.NoTracking : MergeOption.AppendOnly, parameters); }


        public int ExecuteStoredProcedure(string storedProcedureName, params ObjectParameter[] parameters)
        { return ObjectContext.ExecuteFunction(storedProcedureName, parameters); }

        public ObjectResult<TElement> ExecuteStoredProcedure<TElement>(string storedProcedureName, params ObjectParameter[] parameters) where TElement : new()
        { return ObjectContext.ExecuteFunction<TElement>(storedProcedureName, parameters); }

        public ObjectResult<TEntity> ExecuteStoredProcedure<TEntity>(string storedProcedureName, bool asReadOnly, params ObjectParameter[] parameters) where TEntity : class, new()
        { return ObjectContext.ExecuteFunction<TEntity>(storedProcedureName, asReadOnly ? MergeOption.NoTracking : MergeOption.AppendOnly, parameters); }
        

        public virtual void Commit()
        { ObjectContext.SaveChanges(); }
    }
    

    // adapted from: http://www.thomaslevesque.com/2010/10/03/entity-framework-using-include-with-lambda-expressions/
    public class PropertyPathBuildingExpressionVisitor : ExpressionVisitor
    {
        public const string DefaultPropertyPathDelimiter = ".";

        private Stack<string> propertyNameStack = null;


        public Expression Expression
        { get; private set; }

        public string PropertyPath
        { get; private set; }


        private void BuildPropertyPath(string propertyPathDelimeter)
        {
            propertyNameStack = new Stack<string>();

            Visit(Expression);

            PropertyPath = propertyNameStack.Aggregate(
                new StringBuilder(),
                (sb, name) => (sb.Length > 0 ? sb.Append(propertyPathDelimeter) : sb).Append(name)
            ).ToString();

            propertyNameStack = null;
        }


        protected override Expression VisitMember(MemberExpression expression)
        {
            if (propertyNameStack != null)
                propertyNameStack.Push(expression.Member.Name);
            return base.VisitMember(expression);
        }


        protected override Expression VisitMethodCall(MethodCallExpression expression)
        {
            var visitedExpression = (Expression)expression;

            if (expression.Method.IsLinqOperator())
            {
                for (int i = 1; i < expression.Arguments.Count; i++)
                    Visit(expression.Arguments[i]);
                Visit(expression.Arguments[0]);
            }
            else
                visitedExpression = base.VisitMethodCall(expression);

            return visitedExpression;
        }


        public PropertyPathBuildingExpressionVisitor(Expression expression, string delimitedBy = null)
        {
            Expression = expression;
            BuildPropertyPath(delimitedBy ?? DefaultPropertyPathDelimiter);
        }
    }


    public static class ExpressionExtensions
    {
        public static string AsPropertyPath(this Expression expression, string delimitedBy = null)
        { return new PropertyPathBuildingExpressionVisitor(expression, delimitedBy).PropertyPath; }
    }
    

    public class UnableToRemoveEntityDueToRelationshipConstraintsException : Exception
    {
        public UnableToRemoveEntityDueToRelationshipConstraintsException(string message, Exception exception) : base(message, exception)
        { }

        public UnableToRemoveEntityDueToRelationshipConstraintsException(Exception exception) : this(Resources.CannotRemoveEntityDueToRelationshipConstraintsMessage, exception)
        { }

        public UnableToRemoveEntityDueToRelationshipConstraintsException(string message) : base(message)
        { }

        public UnableToRemoveEntityDueToRelationshipConstraintsException() : base()
        { }
    }


    public class DbContextBasedDomainContext : ObjectContextBasedDomainContext
    {
        private static readonly string DeleteStatementConflictedMessagePrefix = "The DELETE statement conflicted";


        private DbContext dbContext = null;

        
        protected override ObjectContext ObjectContext
        { get { return ((IObjectContextAdapter)dbContext).ObjectContext; } }


        protected ExceptionManager ExceptionManager
        { get; private set; }

        protected string DefaultExceptionPolicy
        { get; set; }


        protected override void OnDisposeExplicit()
        {
            base.OnDisposeExplicit();
            dbContext.Dispose();
            dbContext = null;
        }


        public override void Commit()
        {
            ExceptionManager.Process(
                () =>
                {
                    try
                    { base.Commit(); }
                    catch (UpdateException ex)
                    {
                        if ((ex.InnerException != null) && (ex.InnerException.Message.StartsWith(DeleteStatementConflictedMessagePrefix)))
                            throw new UnableToRemoveEntityDueToRelationshipConstraintsException(ex);
                        else
                            throw;
                    }
                },
                DefaultExceptionPolicy
            );
        }


        public DbContextBasedDomainContext(DbContext dbContext, ExceptionManager exceptionManager)
        {
            Guard.AgainstNull(dbContext, "dbContext");
            Guard.AgainstNull(exceptionManager, "exceptionManager");

            this.dbContext = dbContext;
            this.ExceptionManager = exceptionManager;

            this.DefaultExceptionPolicy = Rollins.Framework.ExceptionPolicy.ShieldFromDataAccessExceptions; 
        }
    }


    public static class DbQueryExtensions
    {
        public static DbQuery<TResult> Include<TResult>(this DbQuery<TResult> query, IEnumerable<Expression<Func<TResult, object>>> propertyAccessingExpressions)
        {
            foreach (var propertyAccessingExpression in propertyAccessingExpressions)
                query = query.Include(propertyAccessingExpression.AsPropertyPath());
            return query;
        }

        public static DbQuery<TResult> Include<TResult>(this DbQuery<TResult> query, params Expression<Func<TResult, object>>[] propertyAccessingExpressions)
        { return query.Include((IEnumerable<Expression<Func<TResult, object>>>) propertyAccessingExpressions); }
    }
    

Friday, June 1, 2012

Some useful Entity Framework extension methods

As I've been working with the latest version of Entity Framework, I wanted to go ahead and share some useful code I've found online and created for myself.

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

    public static class MethodInfoExtensions
    {
        // adapted from: http://www.thomaslevesque.com/2010/10/03/entity-framework-using-include-with-lambda-expressions/
        public static bool IsLinqOperator(this MethodInfo method)
        {
            return ((method.DeclaringType == typeof(Queryable)) || (method.DeclaringType == typeof(Enumerable)))
                && (Attribute.GetCustomAttribute(method, typeof(ExtensionAttribute)) != null);
        }
    }


    // adapted from: http://www.thomaslevesque.com/2010/10/03/entity-framework-using-include-with-lambda-expressions/
    public class PropertyPathBuildingExpressionVisitor : ExpressionVisitor
    {
        public const string DefaultPropertyPathDelimiter = ".";

        private Stack<string> propertyNameStack = null;


        public Expression Expression
        { get; private set; }

        public string PropertyPath
        { get; private set; }


        private void BuildPropertyPath(string propertyPathDelimeter)
        {
            propertyNameStack = new Stack<string>();

            Visit(Expression);

            PropertyPath = propertyNameStack.Aggregate(
                new StringBuilder(),
                (sb, name) => (sb.Length > 0 ? sb.Append(propertyPathDelimeter) : sb).Append(name)
            ).ToString();

            propertyNameStack = null;
        }


        protected override Expression VisitMember(MemberExpression expression)
        {
            if (propertyNameStack != null)
                propertyNameStack.Push(expression.Member.Name);
            return base.VisitMember(expression);
        }


        protected override Expression VisitMethodCall(MethodCallExpression expression)
        {
            var visitedExpression = (Expression)expression;

            if (expression.Method.IsLinqOperator())
            {
                for (int i = 1; i < expression.Arguments.Count; i++)
                    Visit(expression.Arguments[i]);
                Visit(expression.Arguments[0]);
            }
            else
                visitedExpression = base.VisitMethodCall(expression);

            return visitedExpression;
        }


        public PropertyPathBuildingExpressionVisitor(Expression expression, string delimitedBy = null)
        {
            Expression = expression;
            BuildPropertyPath(delimitedBy ?? DefaultPropertyPathDelimiter);
        }
    }


    public static class ExpressionExtensions
    {
        public static string AsPropertyPath(this Expression expression, string delimitedBy = null)
        { return new PropertyPathBuildingExpressionVisitor(expression, delimitedBy).PropertyPath; }

        public static string AsConventionalHtmlInputName(this Expression expression)
        { return expression.AsPropertyPath(delimitedBy: "__"); }      
    }


    public static class TypeExtensions
    {
        public static bool IsSubclassOfGenericTypeWithDefinition(this Type type, Type genericTypeDefinition)
        {
            Guard.AgainstNull(type, "type");
            Guard.Against(genericTypeDefinition, gtd => !gtd.IsGenericTypeDefinition, "Must be a generic type definition", "genericTypeDefinition");
            var isOrNot = false;
            var baseType = type.BaseType;

            while (baseType != null && !isOrNot)
            {
                if (baseType.IsGenericType)
                    isOrNot = (baseType.GetGenericTypeDefinition() == genericTypeDefinition);
                baseType = baseType.BaseType;
            }

            return isOrNot;
        }

        public static bool IsEntityTypeConfiguration(this Type type)
        { return type.IsSubclassOfGenericTypeWithDefinition(typeof(EntityTypeConfiguration<>)); }

        public static bool IsComplexTypeConfiguration(this Type type)
        { return type.IsSubclassOfGenericTypeWithDefinition(typeof(ComplexTypeConfiguration<>)); }
    }


    public static class DbQueryExtensions
    {
        public static DbQuery<TResult> Include<TResult>(this DbQuery<TResult> query, IEnumerable<Expression<Func<TResult, object>>> propertyAccessingExpressions)
        {
            foreach (var propertyAccessingExpression in propertyAccessingExpressions)
                query = query.Include(propertyAccessingExpression.AsPropertyPath());
            return query;
        }

        public static DbQuery<TResult> Include<TResult>(this DbQuery<TResult> query, params Expression<Func<TResult, object>>[] propertyAccessingExpressions)
        { return query.Include((IEnumerable<Expression<Func<TResult, object>>>) propertyAccessingExpressions); }
    }


    public static class DbModelBuilderExtensions
    {
        private static MethodInfo GetConfigurationRegistrarAddMethodWithParameter(Type configurationType)
        {
            Guard.AgainstNull(configurationType, "configurationType");
            return typeof(ConfigurationRegistrar).GetMethods().First(method =>
            {
                var isOrNot = string.Equals(method.Name, "Add");
                if (isOrNot)
                {
                    var parameters = method.GetParameters();
                    isOrNot = parameters.First().ParameterType.GetGenericTypeDefinition() == configurationType;
                }
                return isOrNot;
            });
        }

        private static readonly MethodInfo AddEntityTypeConfigurationMethod =
            GetConfigurationRegistrarAddMethodWithParameter(typeof(EntityTypeConfiguration<>));

        private static readonly MethodInfo AddComplexTypeConfigurationMethod =
            GetConfigurationRegistrarAddMethodWithParameter(typeof(ComplexTypeConfiguration<>));


        public static void AddConfigurationsDefinedWithin(this DbModelBuilder dbModelBuilder, Type type)
        {
            Guard.AgainstNull(dbModelBuilder, "dbModelBuilder");
            Guard.AgainstNull(type, "type");

            foreach (var configurationType in type
                .GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)
                .Where(nestedType => nestedType.IsComplexTypeConfiguration()))
            {
                AddComplexTypeConfigurationMethod.MakeGenericMethod(configurationType.BaseType.GenericTypeArguments.First())
                    .Invoke(dbModelBuilder.Configurations, new object[] { Activator.CreateInstance(configurationType) });
            }


            foreach (var configurationType in type
                .GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)
                .Where(nestedType => nestedType.IsEntityTypeConfiguration()))
            {
                AddEntityTypeConfigurationMethod.MakeGenericMethod(configurationType.BaseType.GenericTypeArguments.First())
                    .Invoke(dbModelBuilder.Configurations, new object[] { Activator.CreateInstance(configurationType) });
            }
        }


        public static void AddConfigurationsDefinedWithin(this DbModelBuilder dbModelBuilder, object @object)
        {
            Guard.AgainstNull(@object, "object");
            dbModelBuilder.AddConfigurationsDefinedWithin(@object.GetType());
        }
    }

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;
        }
    }