Sony Arouje

a programmer's log

Generic Entity Framework Repository with Eager Loading

with 7 comments

In this post am going to explain how we can write Generic repository for Entity framework. The implementation is based on the Repository pattern. Inspired by one of the blog by hibernatingrhinos 

Below code explains the Interface I used for creating the repository

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace Generic.Repository.Repository
{
    public interface IRepository
    {
        IQueryable<TEntity> GetQuery<TEntity>() where TEntity : class;
        IList<TEntity> GetAll<TEntity>() where TEntity : class;
        IList<TEntity> Find<TEntity>(Expression<Func<TEntity, bool>> criteria) where TEntity : class;
        void Add<TEntity>(TEntity entity) where TEntity : class;
        void Attach<TEntity>(TEntity entity) where TEntity : class;
        void SaveChanges();
        bool EnableEagerLoading { get; set; }
    }
}

I only included only limited functionality to this Interface. You can extend the interface to add more functionality. Let’s see the implementation

using System;
using System.Collections.Generic;
using System.Data.Objects;
using System.Linq;
using System.Reflection;
namespace Generic.Repository.Repository
{
    public class GenericRepository:IRepository
    {
        string _connectionString = string.Empty;
        public GenericRepository(string connection)
        {
            _connectionString = connection;
        }
        #region Private methods
        private GenericContext _context = null;
        private GenericContext Context
        {
            get
            {
                if (_context == null)
                {
                    _context = GenericContext.GetContext(_connectionString);
                    _context.ContextOptions.LazyLoadingEnabled = false;
                }
                return _context;
            }
        }
        private IList<TEntity> LoadNavigationFields<TEntity>(IList<TEntity> entities) where TEntity : class
        {
            foreach (TEntity entity in entities)
            {
                PerformEagerLoading<TEntity>(entity, this.Context);
            }
            return entities;
        }
        private TEntity LoadNavigationFields<TEntity>(TEntity entity) where TEntity : class
        {
            PerformEagerLoading<TEntity>(entity, this.Context);
            return entity;
        }
        private void PerformEagerLoading<TEntity>(TEntity entity, ObjectContext context) where TEntity : class
        {
            PropertyInfo[] properties = typeof(TEntity).GetProperties();
            foreach (PropertyInfo property in properties)
            {
                object[] keys = property.GetCustomAttributes(typeof(NavigationFieldAttribute), true);
                if (keys.Length > 0)
                {
                    context.LoadProperty(entity, property.Name);
                }
            }
        }
        #endregion

        #region Generic Repository methods
        public IQueryable<TEntity> GetQuery<TEntity>() where TEntity : class
        {
            return this.Context.CreateGenericObjectSet<TEntity>();
        }

        public IList<TEntity> GetAll<TEntity>() where TEntity : class
        {
            IList<TEntity> entities = this.GetQuery<TEntity>().AsEnumerable<TEntity>().ToList();
            if (this._enableEagerLoading == false)
            {
                return entities;
            }
            else
            {
                return this.LoadNavigationFields<TEntity>(entities);
            }
        }

        public IList<TEntity> Find<TEntity>(System.Linq.Expressions.Expression<Func<TEntity, bool>> criteria) where TEntity : class
        {
            IList<TEntity> entities = this.GetQuery<TEntity>().Where(criteria).ToList();
            if (this._enableEagerLoading == false)
            {
                return entities;
            }
            else
            {
                return this.LoadNavigationFields<TEntity>(entities);
            }
        }

        public void Add<TEntity>(TEntity entity) where TEntity : class
        {
            this.Context.CreateGenericObjectSet<TEntity>().AddObject(entity);
        }
        public void Attach<TEntity>(TEntity entity) where TEntity : class
        {
            this.Context.CreateGenericObjectSet<TEntity>().Attach(entity);
        }
        public void SaveChanges()
        {
            this.Context.SaveChanges();
        }

        bool _enableEagerLoading = false;
        public bool EnableEagerLoading
        {
            get { return _enableEagerLoading; }
            set { _enableEagerLoading = value; }
        }
        #endregion
    }
}

The above implementation can be used in most of the CRUD operations. If you have any specific logic for your repository then you can extend it by implementing the IRepository interface. Most of the functionality doesn’t required much explanation except PerformEagerLoading().

Normally we load related entities using LoadProperty in the context or use Include in the Linq query. Both approach wont work here in generic approach as we do not know the properties to do this. I searched a lot to achieve eager loading in a generic mode. But nothing worked then thought of implementing one myself. I haven’t verified the performance of this approach but worked well for my scenario.

The approach I come with is, decorate the related entities with an attribute and use the reflection and iterate through the entity and load it using LoadProperty of context object. Below code will explain my approach.

private void PerformEagerLoading<TEntity>(TEntity entity, ObjectContext context) where TEntity : class
{
    PropertyInfo[] properties = typeof(TEntity).GetProperties();
    foreach (PropertyInfo property in properties)
    {
        object[] keys = property.GetCustomAttributes(typeof(NavigationFieldAttribute), true);
        if (keys.Length > 0)
        {
            context.LoadProperty(entity, property.Name);
        }
    }
}

 

NavigationFieldAttribute is a new attribute inherited from System.Attribute. You can see the implementation in my source code. For the implementation I used the same db model explained in my previous post.

Let’s write a test to verify our GenericRepository

string connectionString = "Data Source=PROLAPE00700\\SQLserver;Initial Catalog=ImagePublisher;User ID=sony;PWD=sony;MultipleActiveResultSets=True;";

[TestMethod()]
public void GetAllTest()
{
    IRepository genericRepository = new GenericRepository(connectionString);
    genericRepository.EnableEagerLoading = true;
    IList<User> users = genericRepository.GetAll<User>();
    Assert.AreNotEqual(0, users.Count);
}

[TestMethod]
public void FindTest()
{
    IRepository genericRepository = new GenericRepository(connectionString);
    genericRepository.EnableEagerLoading = true;
    IList<User> users = genericRepository.Find<User>(u => u.UserID == 1);
    User user = users[0];
    Assert.AreNotEqual(0, user.UserRoles.Count);
}

 

You can understand the EagerLoading functionality by setting false to EnableEagerLoading  in the FindTest above. If we do so the test will fail as it wont load User.UserRoles.

The advantage of this generic approach is we don’t want to create Repository object for each entity if you want to fetch a different entity in the same function. For e.g

IList<User> users=genericRepository.GetAll<Users>();

IList<Role> roles=genericRepository.GetAll<Role>();

Download the source code

Written by Sony Arouje

October 15, 2010 at 5:20 pm

7 Responses

Subscribe to comments with RSS.

  1. […] used the same generic repository pattern explained in one of my previous post. When I wrote this app I spend most of the time in investigating about different methods of […]

  2. […] similar to the one I wrote for EF. You can find the generic repository for EF in one of my post. Below is the Generic repository class for […]

  3. […] I used the Generic repository model I explained earlier in my blog. The above method is a synchronous call and the caller should wait till the db calls complete. Am […]

  4. […] created a generic repository sort of class around db4o, the same way I did one for Entity Framework. This generic repository reduced lot of my work and act as the entry point to persist my entities […]

  5. […] created a generic repository for Hibernate just like the one I created for Entity Framework. Let’s go through the hibernate […]

  6. Hi… Can please help me to call StoredProcedure with help of genericrepository. I am able to call directly means with the help of context object but not finding any way to call with the help of GenericRepository. What i found that when SP imported its create complexType instead of Entity.

    Thanks in advanced.

    Anonymous

    June 25, 2012 at 2:42 pm

    • Hi,
      I am sorry I haven’t connected an ORM with any StoredProc, so not sure how to deal with it in the Generic Repository.

      Regards,
      Sony Arouje

      Sony Arouje

      June 25, 2012 at 3:08 pm


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: