Caching WCF RIA Services data on the server

Share on Facebook

 

In a previous post on impleneting caching with the Entity Framework 4.1, I approached the task by creating an implementation of IDbSet<TEntity> which is the interface implemented and used by DbContext to maintain entity sets with the concrete DbSet<TEntity>. However, my actual requirement was to cache RIA service data on the server side and although abstracting the caching mechanism to the underlying data access technology would have been elegant and more reusable, I ended up using a simpler less elegant solution which got the job done for this particular project.

So what is this simpler approach? Well I introduced a cache field on the RIA Service class like so:

private ICacheProvider Cache = new DefaultCacheProvider();

The ICaheProvider interface could be as bloated or succint as you like but because RIA services does not support generic types (The underlying transport mechanism of WCF does not allow for a schema type supporting generics. Everything must be concrete classes), I opted for a plain and simple interface as shown below:

    public interface ICacheProvider
    {
        object Get(string key);
        void Set(string key, object data, int cacheTime);
        bool IsSet(string key);
        void Invalidate(string key);
    }

The default implementation of the ICacheProvider interface using the .Net framework 4.0 caching objects ObjectCache and MemoryCache is as follows:

public class DefaultCacheProvider : ICacheProvider
    {
        private ObjectCache Cache { get { return MemoryCache.Default; } }

        public object Get(string key)
        {
            return Cache[key];
        }

        public void Set(string key, object data, int cacheTime)
        {
            CacheItemPolicy policy = new CacheItemPolicy();
            policy.AbsoluteExpiration = DateTime.Now + TimeSpan.FromSeconds(cacheTime);
            Cache.Add(new CacheItem(key, data), policy);
        }

        public bool IsSet(string key)
        {
            return (Cache[key] != null);
        }

        public void Invalidate(string key)
        {
            Cache.Remove(key);
        }
    }

In the RIA services class, a cache key is used to keep track of each unique set of data. It is important to note that, the cached objects cannot be IQueryable<TEntity> because entity graphs would not be fully materialised if we cache the IQueryable objects, rather we have to execute the queries by calling ToList() extension on the query and then cache the actual list of objects. When we fetch data, we first check the cache for an existing set, and notice also the way we check the cache: We cast the object at the time of checking rather than check if the object is in the cache(by using the boolean IsSet interface method), this is because the little time delay in checking before fetching could pose problems in multi-threaded environments.

Finally, we invalidate the cache on any data modification service call. The relevant code for an entity called "Project" maintained through the RIA service class is shown below:

        // TODO:
        // Consider constraining the results of your query method.  If you need additional input you can
        // add parameters to this method or create additional query methods with different names.
        // To support paging you will need to add ordering to the 'Projects' query.
        [AdminUsersOnly]
        public IQueryable<Project> GetProjects()
        {
            return this.DbContext.Projects.Include("Phases.PhaseType").OrderBy(p => p.StartDate);           �
        }

        public IQueryable<Project> GetActiveProjects()
        {           �
            var projects = (List<Project>)Cache.Get("ACTIVEPROJECTS");
            if (projects == null)
            {               �
                projects = this.DbContext.Projects.Include("Phases.PhaseType").Where(p => p.Phases.Any(ph => ph.PhaseDate >= DateTime.Today)).OrderBy(p => p.Phases.OrderBy(ph => ph.PhaseDate).FirstOrDefault().PhaseDate).ToList();

                if (projects.Any())
                {
                    Cache.Set("ACTIVEPROJECTS", projects, cacheDurationInSecs);
                }
            }
            return projects.AsQueryable();
        }

        [AdminUsersOnly]
        public void InsertProject(Project project)
        {
            Cache.Invalidate("ACTIVEPROJECTS");
            DbEntityEntry<Project> entityEntry = this.DbContext.Entry(project);
            if ((entityEntry.State != EntityState.Detached))
            {
                entityEntry.State = EntityState.Added;
            }
            else
            {
                this.DbContext.Projects.Add(project);
            }
        }

        [AdminUsersOnly]
        public void UpdateProject(Project currentProject)
        {
            Cache.Invalidate("ACTIVEPROJECTS");
            this.DbContext.Projects.AttachAsModified(currentProject, this.ChangeSet.GetOriginal(currentProject), this.DbContext);
        }

        [AdminUsersOnly]
        public void DeleteProject(Project project)
        {
            Cache.Invalidate("ACTIVEPROJECTS");
            DbEntityEntry<Project> entityEntry = this.DbContext.Entry(project);
            if ((entityEntry.State != EntityState.Deleted))
            {
                entityEntry.State = EntityState.Deleted;
            }
            else
            {
                this.DbContext.Projects.Attach(project);
                this.DbContext.Projects.Remove(project);
            }
        }

 

It might not be the most elegant, but the project I was working on was time constrained, and under the circumstances, this got the job done.
Hope you find it useful.

 


Implementing an IDbSet to support caching with Entity Framework 4.1

Share on Facebook

Using generic Repositories (Repository<TEntity>) with your Data Access Layer (DAL) provides an elegant approach to isolating your DAL/ORM (Object Relational Mapper) technology from your upper layers such as the Business logic and the UI.

Entity Framework 4.1 code first in particular lends itself to the repository pattern and in particular the generic repository pattern.

However when working with RIA Services, the client side code generated by the RIA services calls back into the server side RIA services with expected service endpoints. These end points are dificult to map with a generic repository in the server side RIA services.

With respect to a silverlight RIA application I am currently working on, I needed to implement some entity level auditing and also server side caching. Since the repository pattern was not working well with RIA services, I hooked up the auditing as an interceptor before the actual DbContext.SaveChanges method call. The approach used is very well documented in this post.

Next was caching, I decided to implement an IDbSet by using the decorator pattern. I figured the implementation would replace the DBSet<TEntity> which maintains the various entity sets in my DbContext derived context class.

My current implementation looks like so:

public class CachedDbSet : IDbSet, IOrderedQueryable, IOrderedQueryable, IQueryable, IQueryable, IEnumerable, IEnumerable, IListSource  where TEntity : class
{
    private readonly DbSet Set;
    private IQueryable CachedSet;

    private readonly ICacheProvider cacheProvider = new CacheProvider();
    private int cacheDurationInSecs = 3600;

    public CachedDbSet(DbContext context) : this(context, "")
    {
    }

    public CachedDbSet(DbContext context, string includeProperty)
    {
        Set = context.Set();
        
        if (!String.IsNullOrEmpty(includeProperty))
        {
            CachedSet = context.Set().Include(includeProperty);
        }
        else 
        {
            CachedSet = Set;
        }
    }    

    #region cached sets
    
    private IQueryable All
    {
        get
        {
            InvalidateCache();
            var key = typeof(TEntity).ToString();
            return cacheProvider.GetOrStore>(key, () => CachedSet, cacheDurationInSecs);
        }
    }

    #endregion

    public Func MatchesFilter { get; private set; }    

    public TEntity Add(TEntity entity)
    {
        InvalidateCache();
        return Set.Add(entity);
    }

    public TEntity Attach(TEntity entity)
    {
        InvalidateCache();
        return Set.Attach(entity);
    }

    public TDerivedEntity Create() where TDerivedEntity : class, TEntity
    {
        var entity = Set.Create();
        return (TDerivedEntity)entity;
    }

    public TEntity Create()
    {
        var entity = Set.Create();
        return entity;
    }

    public TEntity Find(params object[] keyValues)
    {
        var entity = ((DbSet)All).Find(keyValues);
        if (entity == null)
        return null;
        return entity;
    }

    public TEntity Remove(TEntity entity)
    {
        InvalidateCache();
        return Set.Remove(entity);
    }

    public ObservableCollection Local { get { return Set.Local; } }

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

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

    Type IQueryable.ElementType { get { return typeof(TEntity); } }

    Expression IQueryable.Expression { get { return Set.AsQueryable().Expression; } }

    IQueryProvider IQueryable.Provider { get { return Set.AsQueryable().Provider; } }

    bool IListSource.ContainsListCollection { get { return false; } }

    IList IListSource.GetList() { throw new InvalidOperationException(); }   

    public void AttachAsModified(TEntity entity, TEntity original, DbContext context)
    {
        InvalidateCache();
        Set.AttachAsModified(entity, original, context);
    }

    public IQueryable Include(string propertyName)
    {
        return All.Include(propertyName);
    }

    private void InvalidateCache()
    {
        cacheProvider.Invalidate(typeof(TEntity).ToString());
    }
}

The above CachedDbSet<TEntity> may then be used in the Entity Framework 4.1 derive DbContext class like so:

private CachedDbSet projects;
public CachedDbSet Projects
{
    get
    {
        if (projects == null)
            projects = new CachedDbSet(this, "Phases.PhaseType");
        return projects;
    }
    set
    {
        projects = value;
    }
}

Where Project is an entity in the DB.

Unfortunately, I have hit a brick wall in the sense that, the context used to generate the
wrapped DbSet in the constructor is disposed before RIA services tries to make any updates, so
the AttachAsModified inner call on the AttachAsModified method fails. I have tried for over an
hour to find an alternative with no joy.

If you have a way around this, please let me know by posting a comment. I have engaged the CachedDbSet implementation but you will notice I invalidate the cache before checking it on the "All" property. So effectively short circuiting it.

Next to implement is Remote Logging with the new Enterprise Library Silverlight Integration pack. Stay tuned.


blockUI Configuration

Share on Facebook

 

 

$.blockUI.defaults.message = "<h1><img style='vertical-align:middle;' src='" + document.getElementById("ApplicationRootForJS").href + "scripts/pages/images/spinner.gif' /> processing, please wait...</h1>";

 

$(document).ajaxStart($.blockUI).ajaxStop($.unblockUI);


Using a Description Attribute with Enumerations

Share on Facebook

Simple extension class to help use the description attribute with enumeration instances

public static class Extensions
    {
        public static string ToDescriptionString(this Enum currentEnum)
        {
            var fi = currentEnum.GetType().GetField(currentEnum.ToString());
            var da = (DescriptionAttribute)Attribute.GetCustomAttribute(fi, typeof(DescriptionAttribute));
            return da != null ? da.Description : currentEnum.ToString();
        }

        public static object ParseWithDescriptionAttribute(this System.Enum value, string valueToParse)
        {
            //Get the Description String Attribute if any exists on the enum value
            string descString = value.ToDescriptionString();
            if (descString == valueToParse || value.ToString() == valueToParse)
            {
                return Enum.Parse(value.GetType(), value.ToString());
            }
            else return null;
        }
    }

Convert Enum to SelectList

Share on Facebook

Simple extension method to convert enumeration to a selectList for use with MVC DropDownList.

       
        public static SelectList ToSelectList(this Enum enumeration)
        {
            var list = (from Enum d in Enum.GetValues(enumeration.GetType())
                        select new { ID = (int)Enum.Parse(enumeration.GetType(),    Enum.GetName(enumeration.GetType(), d)), Name = d.ToString() }).ToList();
            return new SelectList(list, "ID", "Name");
        }

Say you have an enumeration depicted in the code below:

 
 public enum Options
 {
    option1= 1,
    option2= 2,
    option3=3
 }     

You will use the above extension in this way:

 
 SelectList Selections;
 Options opts = new Options();
 Selections = opts.ToSelectList();

Unit of Work with Lazy loaded Repositories (EF Code First)

Share on Facebook

The Unit of Work pattern is particularly useful for managing data context disposal when using generic repositories. There are different approaches online for using the Unit of Work pattern with Asp.Net MVC Controllers. Some developers pass in a Unit of Work interface as well as a repository interface as parameters to the controller. Others prefer to manifest the data repositories (generic), as properties of the Unit of Work. In most cases where the latter is used, I find most people pre-define the repositories. ie, iUoW.productRepository or iUoW.customerRepository. So I set out to create a generic lazy loaded GetRepository<T> on the UoW interface, to load the Repository<T> data repositories on demand.

The interface is defined as follows:

 public interface IUoW
    {
        IRepository GetRepository() where TEntity : class, new();
        void Commit();
        void Dispose();
    }

And it is implemented like so:

using System;
using System.Collections.Generic;
using RollCall.Entities;

namespace RollCall.Pages.Model
{
    public class UoW : IDisposable, IUoW
    {
        private readonly RollCallContext _context = new RollCallContext();
        private static readonly IDictionary Repos = new Dictionary();

        public IRepository GetRepository() where TEntity : class, new()
        {
            if (Repos != null)
            {
                if (!Repos.ContainsKey(typeof(TEntity)))
                {
                    var repo = new Repository(_context);
                    Repos.Add(typeof(TEntity), repo);
                }
                return (Repository)Repos[typeof(TEntity)];
            }
            else return null;
        }

        public void Commit()
        {
            _context.SaveChanges();
        }

       #region idisposable
        private bool disposed = false;

        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    _context.Dispose();
                }
            }
            this.disposed = true;
        }

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

Where the generic repository is defined as:

public interface IRepository where TEntity : class,new()
    {
        IEnumerable Get(
            Expression> filter = null,
            Func, IOrderedQueryable> orderBy = null,
            string includeProperties = "");

        TEntity GetById(object id);
        void Insert(TEntity entity);
        void Delete(object id);
        void Delete(TEntity entityToDelete);
        void Update(TEntity entityToUpdate);
    }

And the generic repository is implemented like so:

public class Repository : IRepository where TEntity : class, new()
    {
        internal RollCallContext Context;
        internal DbSet DbSet;

        public Repository(RollCallContext context)
        {
            this.Context = context;
            this.DbSet = context.Set();
        }

        public virtual IEnumerable Get(
            Expression> filter = null,
            Func, IOrderedQueryable> orderBy = null,
            string includeProperties = "")
        {
            IQueryable query = DbSet;

            if (filter != null)
            {
                query = query.Where(filter);
            }

            foreach (var includeProperty in includeProperties.Split
                (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
            {
                query = query.Include(includeProperty);
            }

            if (orderBy != null)
            {
                return orderBy(query).ToList();
            }
            else
            {
                return query.ToList();
            }
        }

        public virtual TEntity GetById(object id)
        {
            return DbSet.Find(id);
        }

        public virtual void Insert(TEntity entity)
        {
            DbSet.Add(entity);
        }

        public virtual void Delete(object id)
        {
            TEntity entityToDelete = DbSet.Find(id);
            Delete(entityToDelete);
        }

        public virtual void Delete(TEntity entityToDelete)
        {
            if (Context.Entry(entityToDelete).State == EntityState.Detached)
            {
                DbSet.Attach(entityToDelete);
            }
            DbSet.Remove(entityToDelete);
        }

        public virtual void Update(TEntity entityToUpdate)
        {
            DbSet.Attach(entityToUpdate);
            Context.Entry(entityToUpdate).State = EntityState.Modified;
        }
    }

Where RollCallContext is a subclass of the Entity Framework Code First base class DbContext.

Please let me know if there are any pitfalls with the approach by leaving your comments.


paypal buttons extension for blogengine

Share on Facebook

Whereas wordpress has plugins for stuff like paypal donate and buy now buttons in abundance,

Blogengine is far behind the competition for stuff like that. I know people might have developed such extensions, but are probably not yet willing to share them with fellow blogengine users. I thought I will change that so I am releasing my paypal buttons for blogengine extension for you all to use.

Technically, the extensions are simple and just wrap the paypal html for the buttons in .Net code and provide an easy setup via the extensions manager in blogengine.

EXAMPLE:

The donate button on this site is an example usage of the donate button extension.

USAGE SYNTAX: [--buynow:Gold Watch;ID00125;12.50] without the --, results in the button below:

Make payments with PayPal - it

DOWNLOAD:

You may download the extensions below. To install the extensions, simply copy the downloaded file into your /App_Code/Extensions folder of your blogengine site root folder.

 

PaypalAddToBasketButtons.cs (6.72 kb)

PaypalBuyNowButtons.cs (6.13 kb)

PaypalDonateButton.cs (5.49 kb)

 

NEW:

Nuget packages for the Paypal Buttons: 

BlogEnginePaypalDonateButton.1.0.nupkg (4.89 kb)

BlogEnginePaypalAddToBasketButton.1.0.nupkg (5.49 kb)

BlogEnginePaypalBuyNowButton.1.0.nupkg (5.24 kb)

 

Aso available from the blogEngine gallery @ http://dnbegallery.org/cms/List/ByCategory/Extensions/Miscellaneous

If you need help on how to use the nuget packages in blogengine, check http://dnbegallery.org/cms

If you want to drop the extension onto a production server then extract the .cs file from the nuget package and drop it in /App_Code/Extensions folder of your blogEngine root folder.

You can then make the necessary settings in Extensions Manager:

 

If you get an error about the seller only accepts encrypted payments, then do the following:

 

1. Log in to your PayPal Account.

2. Click "Profile" tab or "Edit Profile" link.

3. Under "Selling Preferences", select "Website Preferences".

4. In the section titled "Encrypted Website Payments" select "Off".

5. Click "Save".

 

 

Please post comments if you have any bugs or modifications or enhancements.

Cheers!

NB: As usual the software is provided free of charge and use at your own risk. If you have any questions, I will do my best to provide the answers in my free time, if I ever really have any!


Session backed IDbSet<T> for use with Entity framework codefirst

Share on Facebook

With the release of entity framework 4.1 with support for the Code First approach (aka Magic Unicorn Edition), I recently had a need for an in-memory data table for a web application. The Use Case required multiple users to generate item lists in memory and then submit by email to a transaction system.

Since there is a constraint for multiple simultaneous use, I am unable to implement the Entity Framework friendly IDbSet<T> as a list saved in server memory because it will end up being shared across users. What we want is a session backed implementation of IDbSet<T>, on the road to implementing the session backed version though, I found some memory backed implementations online (I have augmented these) which were meant to help test EF code first.

I had to use some reflection to help identify primary key properties. The resulting requirement due to this implementation is that you have to use the DataAnnotations [Key] attribute to mark primary keys on all your entities. An exception will be thrown if the attribute is not found on any property of your entity.

The session backed implementation uses a SessionDataStore class to manage the data in session memory.

 public class SessionDataStore
    {
        private readonly string _sessionDataName;

        public SessionDataStore(string storeName)
        {
            _sessionDataName = storeName;
        }

        public T Add(T item)
        {
           List arr = null;
           if ((HttpContext.Current.Session[_sessionDataName] != null))
            {
                arr = (List)HttpContext.Current.Session[_sessionDataName];
            }
            else
            {
                arr = new List();
                HttpContext.Current.Session[_sessionDataName] = arr;
            }
            arr.Add(item);
            return item;
        }

        public void Remove(int index)
        {
            var items = (List)HttpContext.Current.Session[_sessionDataName];
            if ((index <= items.Count - 1))
            {
                items.RemoveAt(index);
            }            
        }

        public void Remove(T item)
        {
            var items = (List)HttpContext.Current.Session[_sessionDataName];
            if (items.Contains(item))
            {
                items.Remove(item);
            }
        }

        public bool UpdateItem(T itemToUpdate, int index)
        {
            var items = (List)HttpContext.Current.Session[_sessionDataName];
            if (index > items.Count - 1)
            {
                return false;
            }
            else
            {
                foreach (System.Reflection.PropertyInfo pToRead in itemToUpdate.GetType().GetProperties())
                {
                    foreach (System.Reflection.PropertyInfo pToWrite in ((T) items[index]).GetType().GetProperties())
                    {
                        if (pToRead.Name == pToWrite.Name)
                        {
                            pToWrite.SetValue((T) items[index], pToRead.GetValue(itemToUpdate, null), null);
                        }
                    }
                }
                return true;
            }
        }

        public List GetItems()
        {
            return HttpContext.Current.Session == null ? null : (List)HttpContext.Current.Session[_sessionDataName];            
        }

        public T GetItem(int index)
        {
            var items = (List)HttpContext.Current.Session[_sessionDataName];
            return (T)items[index];            
        }

        public int ItemCount()
        {
            var items = (List)HttpContext.Current.Session[_sessionDataName];
            return items == null ? 0 : items.Count;
        }

        public void Clear()
        {
            var items = (List)HttpContext.Current.Session[_sessionDataName];
            if ((items != null) && items.Count > 0)
            {
                items.Clear();
            }
        }
    }

 

The SessionDbSet<T> then uses the SessionDataStore<T> like so:

 public class SessionDbSet : IDisposable, IDbSet where T : class, IEntityState 
    {
        private readonly SessionDataStore _data;
        private static IQueryable _query;
        //private static int _nextKey;

        public SessionDbSet(String storeName)
        {
            _data = new SessionDataStore(storeName);
            var items = _data.GetItems();
            if (items != null)
                _query = _data.GetItems().AsQueryable();
            else _query = new List().AsQueryable(); 
        }

        public virtual T Find(params object[] keyValues)
        {
            return this.SingleOrDefault(e => GetKeyValue(e) == (int)keyValues.FirstOrDefault());
        }

        public T Add(T item)
        {
            if (GetKeyValue(item) == 0)
                SetKeyValue(item);
            _data.Add(item);
            return item;
        }

       public T Remove(T item)
        {
            _data.Remove(item);
            return item;
        }

       public void Remove(int index)
       {
          _data.Remove(index);           
       }

        public T Attach(T item)
        {
            if (item.State == EntityState.Modified)
            {
                var key = (int)GetKeyValue(item);
                var itemToDelete = _data.GetItems().Where(x => GetKeyValue(x) == key).FirstOrDefault();
                Remove(itemToDelete);   
            }                
            _data.Add(item);
            return item;
        }

        public T Create()
        {
            return Activator.CreateInstance();
        }

        public TDerivedEntity Create() where TDerivedEntity : class, T
        {
            return Activator.CreateInstance();
        }

        public ObservableCollection Local
        {
           get
           {
               var items = _data.GetItems();
               return new ObservableCollection(items);
           }
        }

        public T Detach(T item)
        {
            _data.Remove(item);
            return item;
        }

        Type IQueryable.ElementType
        {
            get { return _query.ElementType; }
        }

        System.Linq.Expressions.Expression IQueryable.Expression
        {
            get { return _query.Expression; }
        }

        IQueryProvider IQueryable.Provider
        {
            get { return _query.Provider; }
        }

        IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            var items = _data.GetItems();
            return items.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            var items = _data.GetItems();
            if (items != null)
                return items.GetEnumerator();
            else return new List().GetEnumerator();
        }

        public void Dispose()
        {
            _data.Clear();
        }

        #region helpers
        private void SetKeyValue(T entity)
        {
            PropertyInfo keyProperty = GetKeyProperty(entity);
           if (keyProperty == null)
                throw new ArgumentNullException("The entity " + entity.ToString() + " has no key property, please place the [Key] attribute on the kep property.");            
           keyProperty.SetValue(entity, GetNextKey() ,null);
        }

       private static int GetKeyValue(T entity)
       {
           var keyProperty = GetKeyProperty(entity);
           return (int)keyProperty.GetValue(entity, null);
       }

       private static PropertyInfo GetKeyProperty(T entity)
       {
           var properties = entity.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod);
           return properties.Where(x => Attribute.IsDefined(x, typeof(KeyAttribute))).FirstOrDefault();
       }

        private int GetNextKey()
        {
            if (_data.GetItems() == null || _data.GetItems().Count == 0)
                return 0;
            else
            {
                var highestKeyItem = _data.GetItems().OrderBy(GetKeyProperty).LastOrDefault();
                var lastIndex = GetKeyValue(highestKeyItem);
                return (int)lastIndex + 1;
            }
        }

        #endregion

    }

 

IEntityState is a simply interface which should be implemented by your entities to provide the State property on all entities. The State property is used in the actual EF library to monitor the status of entities within the context.

IEntityState looks like this:

public interface IEntityState
    {
        EntityState State { get; set; }
    }

 

The simple momery backed implementation looks like so:

 public class MemoryDbSet : IDisposable, IDbSet where T : class
    {
        private static HashSet _data;
        private static IQueryable _query;
        private static int _nextKey;

        public MemoryDbSet()
        {
            _data = new HashSet();
            _query = _data.AsQueryable();
        }

        public virtual T Find(params object[] keyValues)
        {
            return this.SingleOrDefault(e => GetKeyValue(e) == keyValues.Single());
        }

        public T Add(T item)
        {
            if (GetKeyValue(item) == null)
                SetKeyValue(item);
            _data.Add(item);
            return item;
        }

       public T Remove(T item)
        {
            _data.Remove(item);
            return item;
        }

        public T Attach(T item)
        {
            _data.RemoveWhere(e => GetKeyValue(e) == GetKeyValue(item));
            _data.Add(item);
            return item;
        }

        public T Create()
        {
            return Activator.CreateInstance();
        }

        public TDerivedEntity Create() where TDerivedEntity : class, T
        {
            return Activator.CreateInstance();
        }

        public ObservableCollection Local
        {
            get { return new ObservableCollection(_data); }
        }

        public T Detach(T item)
        {
            _data.Remove(item);
            return item;
        }

        Type IQueryable.ElementType
        {
            get { return _query.ElementType; }
        }

        System.Linq.Expressions.Expression IQueryable.Expression
        {
            get { return _query.Expression; }
        }

        IQueryProvider IQueryable.Provider
        {
            get { return _query.Provider; }
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return _data.GetEnumerator();
        }

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

        public void Dispose()
        {
            _data.Clear();
        }

        #region helpers
        private static void SetKeyValue(T entity)
        {
            PropertyInfo keyProperty = GetKeyProperty(entity);
           if (keyProperty == null)
                throw new ArgumentNullException("The entity " + entity.ToString() + " has no key property, please place the [Key] attribute on the kep property.");            
            //var nextKey = _data.Any() ? _data.Count + 1 : 1; 
            keyProperty.SetValue(entity, GetNextKey() ,null);
        }

       private static object GetKeyValue(T entity)
       {
           var keyProperty = GetKeyProperty(entity);
           return keyProperty.GetValue(entity, null);
       }

       private static PropertyInfo GetKeyProperty(T entity)
       {
           var properties = entity.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod);
           return properties.Where(x => Attribute.IsDefined(x, typeof(KeyAttribute))).FirstOrDefault();
       }

        private static int GetNextKey()
        {
            _nextKey += 1;
            return _nextKey;
        }

        #endregion

    }

 

Hope this helps.


Generic configuration class for asp.net applications

Share on Facebook

As with most software projects, you certainly need some form of configuration. Although convention over configuration has been widely accepted as a good solution to convoluted and often confusing configuration settings, as a good developer you always should make room for the odd end user whose requirements simply are at odds with all the conventions you have thought off and catered for.

The .net framework provides the ConfigurationManager class which resides in the  System.Configuration namespace of the System.Configuration.dll assembly. This class provides various methods for persisting configuration information either in web.config files for web applications or app.config files for desktop (winforms/WPF) applications.

It is however very easy when you start writing an application to write code like:

ConfigurationManager.AppSettings["SomeSetting"]

It is so easy then to leave such code scattered all over various files in the project and create a maintenance nightmare. What we need is a single point or class responsible for manifesting our configuration information and then load the configuration information at application startup and make that information accessible to other parts of our system through abstractions of it such as interfaces. This will allow for easier maintenance when we need to add more configuration data or scale our project to use an IoC container and also make our configuration test friendly. So how do we go about designing our configuration class, firstly, the configuration data is read from some form of persistence but we will limit this discussion to the web.config/app.config implementations. Specifically, we will use the NameValueSectionHandler to implement our custom configuration. You can create a new config section in the web/app.config file in the following form:

 
   
    
  

This then allows you to create a new configuration section with the new section name "CustomSectionName" like so:

  

    
    
    
    
     
    

Because we need to verify the existence of the setting (eg. TaxRate above) in the persistence source, we need to check this at the time of reading the data. We could do this by doing something like:

  private string configSetting
  public string ConfigSetting {
	get {
	if (string.IsNullOrEmpty(configSetting)) {
		throw new ArgumentNullException("configSetting", "ConfigSetting must be specified in the configuration.");
		}
	     return configSetting;
	    }
	set {
	if (string.IsNullOrEmpty(value)) {
		throw new ArgumentNullException("value", "ConfigSetting must be specified in the configuration.");
		}
	     configSetting = value;
	    }
	}

However, it doesn't take long for us to realise how bloated and unclean our code feels like by having all that boiler plate code repeated for every configuration data encapsulated through a property. So I devised a class to abstract this checking and exception throwing logic away in the form of the generic ConfigurationProperty class listed below:

  public class ConfigurationProperty
    {
        private readonly String _propertyName;
        private T _value;
        public T Value
        {
         get
          {
           if (String.IsNullOrEmpty(_value.ToString()))
            {
             throw new ArgumentNullException(_propertyName, char.ToUpper(_propertyName[0]) + 
		_propertyName.Substring(1) + " must be specified in the configuration.");
                }
                return _value;
            }
            set
            {
            if (String.IsNullOrEmpty(value.ToString()))
             throw new ArgumentNullException(_propertyName, char.ToUpper(_propertyName[0]) + 
		_propertyName.Substring(1) + " must be specified in the configuration.");
                this._value = value;
            }
        }

        public ConfigurationProperty(String propertyName)
        {
            _propertyName = propertyName;
        }
    }

With this class in place, we can then create an interface to represent our configuartion data (modify to suit your needs) as follows. The type of the configuration data is specified as the type parameter to the generic ConfigurationProperty class. A downside to doing this is that we have to access config values through the "Value" property of the ConfigurationProperty class. (eg. EmailSource.Value)

 
  public interface IConfiguration
    {
        ConfigurationProperty EmailSource { get; set; }
        ConfigurationProperty EmailDestination { get; set; }
        ConfigurationProperty SalesAccountCode { get; set; }
        ConfigurationProperty DefaultCarriage { get; set; }
        ConfigurationProperty TaxRate { get; set; }
    }

At this point we are now ready to craft the actual configuration class which will manifest our configuration information. This class has to implement our interface so as to represent our configuration information in the app/web.config file. Also, because we do not want different copies of this config data floating in our application at any one time, we will implement all members of the class as static. In particular, the class will keep a private static copy of the configuration abstraction IConfiguration as "currentConfiguration". It will allow us to pass in a copy of IConfiguration through the static "Configure" method, and allow access to the current config data through a static property "Current". The class will load the configuration from persistence if no Iconfiguration has been passed in at the time of requesting the configuration, otherwise it will use the passed in IConfiguration. The class uses lamdba expressions as a way of matching configuration names to their properties and therefore it is important to match these names. The Configuration class code ends up looking like so:

 
public class Configuration : IConfiguration
    {

        private static IConfiguration _currentConfiguration;

        #region Properties
        public ConfigurationProperty EmailSource { get; set; }
        public ConfigurationProperty EmailDestination { get; set; }
        public ConfigurationProperty SalesAccountCode { get; set; }
        public ConfigurationProperty DefaultCarriage { get; set; }
        public ConfigurationProperty TaxRate { get; set; }
        #endregion

        /// 
        /// Sets up the configuration using a manually generated Configuration instance rather than using the Web.config file. 
        /// 
        /// 
        public static void Configure(IConfiguration configuration)
        {
            _currentConfiguration = configuration;
        }

        /// 
        /// Gets the current configuration. If none has been specified using Configuration.Configure, it is loaded from the web.config
        /// 
        public static IConfiguration Current
        {
            [DebuggerStepThrough]
            get
            {
                if (_currentConfiguration == null)
                    _currentConfiguration = LoadConfigFromFile();
                return _currentConfiguration;
            }
        }

        private static IConfiguration LoadConfigFromFile()
        {
            var configCollection = (NameValueCollection)ConfigurationManager.GetSection("SageOrderEntry");
            if (configCollection == null)
            {
                return new Configuration();
            }
            var cfg = new Configuration();

            cfg.EmailSource = GetConfigValue(x => x.EmailSource, configCollection);
            cfg.EmailDestination = GetConfigValue(x => x.EmailDestination, configCollection);
            cfg.SalesAccountCode = GetConfigValue(x => x.SalesAccountCode, configCollection);
            cfg.TaxRate = GetConfigValue(x => x.TaxRate, configCollection);
            cfg.DefaultCarriage = GetConfigValue(x => x.DefaultCarriage, configCollection);

            return cfg;
        }

        private static ConfigurationProperty GetConfigValue(Expression>> expression, NameValueCollection 	configCollection)
        {
            var body = expression.Body as MemberExpression;
            if (body == null && expression.Body is UnaryExpression)
            {
                body = ((UnaryExpression)expression.Body).Operand as MemberExpression;
            }
            var name = body.Member.Name;
            var cfgProperty = new ConfigurationProperty(name) { Value = (T)Convert.ChangeType(configCollection[name], typeof(T)) };
            return cfgProperty;
        }
    }

In an MVC web application, you may create a dependency property on the controller class and initialise it with a call to the static "Configuration.Current". like so:

 
	private static  IConfiguration _config;

        public MyController()
        {
            //Configuration.Configure(codeConfig); //Use for testing
            _config = Configuration.Current;
        }

In a test environment, you will create an instance of IConfiguration by using the Configuration class and pass the instance into the static Configure method, and then access it through the static Current property like so:

 
 IConfiguration codeConfig = new Configuration
           {
               DefaultCarriage = new ConfigurationProperty("DefaultCarriage"){Value = 7.95M},
               EmailDestination = new ConfigurationProperty("EmailDestination") { Value = xx@yyy.com" },
               EmailSource = new ConfigurationProperty("EmailSource") { Value = "aaa@bbb.com" },
               SalesAccountCode = new ConfigurationProperty("SalesAccountCode") { Value = "PREPAID" },
               TaxRate = new ConfigurationProperty("TaxRate") { Value = 0.175M }
           };

            Configuration.Configure(codeConfig);
            _config = Configuration.Current;

You then can access your configuration data in your controller or repository if passed into the repos as well in the form:

 
  ord.TaxRate = _config.TaxRate.Value;
  ord.TaxAmount = (ord.ShippingAmount + ord.CalculateSubTotal())*ord.TaxRate; 

By wiring your configuration settings this way, you have single point to troubleshoot any configuration problems, plus the code is well presented. Also as a result you have good isolation for testing. Hope you find this useful.

Credit where credit is due, I found the initial approach for doing this in an open source project (SagePayMvc) by Jeremy Skinner. Credit to his brilliance in structuring the Configuration system this way.

Download the complete configuration class with the supporting ConfigurationProperty class and iConfiguration interface below:

Configuration.cs (4.38 kb)


sectionName is case sensitive in ConfigurationManager.GetSection(sectionName)

Share on Facebook

One of those little things which when missed through oversight sometimes can get you wasting so much time looking through your code to find out where things are failing:

When using: ConfigurationManager.GetSection("SectionName"), SectionName is case sensitive,

ConfigurationManager.GetSection("AppSettings") != ConfigurationManager.GetSection"appSettings");
ConfigurationManager.GetSection("AppSettings"); //will always return null

Hope this helps.


About Me

When not scratching my head for solutions to software challenges, I spend my time playing with my little boy - Michael Jnr.

Quotations

"All great things are simple, and many can be expressed in single words: freedom, justice, honor, duty, mercy, hope."
Sir Winston Churchill

Donate with PayPal - it

Calendar

<<  January 2012  >>
MoTuWeThFrSaSu
2627282930311
2345678
9101112131415
16171819202122
23242526272829
303112345

View posts in large calendar

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2005 - 2012

Search