5

I have been learning the Repository and Unit of Work patterns from various sources, including here:

http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application

If you browse to thr link above to Creating a Unit of Work Class there is the following:

    private GenericRepository<Department> departmentRepository;
    private GenericRepository<Course> courseRepository;

The which is fine, but I wanted to have a go at extending the Unit of Work class that Generic and setup a collection of GenericRepositories so i can update then dynamnically based on the model I pass through.

I ultimately want to do the following in my Controller:

public class LedgerUserController : Controller
{
    private GenericUnitOfWork unitOfWork = new GenericUnitOfWork();
    LedgerUser ledgeruser = new LedgerUser();


    public ActionResult Index()
    {
        //var ledgerusers = db.LedgerUsers.Include(l => l.Image).Include(l => l.UserType);
        var view = unitOfWork.Repository(ledgeruser).Get(l => l.LastName == "smith");
        return View(view.ToList());
    }
}

So here is my classes and interfaces so far:

IRepository.cs

/// <summary>
/// Generic Repository for CRUD Operations and methods
/// to locate entities within your store. This is not specific to which Data Access
/// tools your are using (Direct SQL, EF, NHibernate, etc).
/// </summary>
public interface IRepository<T> where T : class
{
    //--Search Operations
    IQueryable<T> GetAll();
    IEnumerable<T> GetAllList();
    IEnumerable<T> Get(Expression<Func<T,bool>> filter);
    T GetIt(Expression<Func<T, bool>> filter);
    T GetById(object id);


    //--CRUD Operations
    void Create(T entity);
    void Update(T entity);
    void Delete(T entity);

}

GenericRepository.cs

/// /// Repository Class for looking afer Entities /// CRUD Operations /// /// public class GenericRepository : IRepository where TEntity : class {

    internal AccountsContext context;
    internal DbSet<TEntity> dbSet;
    internal IQueryable<TEntity> query;

    /// <summary>
    /// Default Constructor.
    /// </summary>
    /// <param name="context"></param>
    public GenericRepository(AccountsContext context)
    {
        this.context = context;
        this.dbSet = context.Set<TEntity>();
    }

    #region Methods
    #region Search Functionality
    /// <summary>
    /// Obtain the whole Entity to query if needed.
    /// </summary>
    /// <returns>IQueryable object.</returns>
    public virtual IQueryable<TEntity> GetAll()
    {
        IQueryable<TEntity> query = dbSet;
        return query;

    }

    /// <summary>
    /// Obtain the whole Entity to Enumerate throught if needed.
    /// </summary>
    /// <returns>IEnumerble object.</returns>
    public virtual IEnumerable<TEntity> GetAllList()
    {
        IQueryable<TEntity> query = dbSet;
        return query.ToList();

    }

    /// <summary>
    /// Locate an Entity by its indexed id.
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    public virtual TEntity GetById(object id)
    {
        return dbSet.Find(id);
    }

    /// <summary>
    /// Gets a collection based on LINQ lambda expressions
    /// </summary>
    /// <param name="filter">Lambda Expression</param>
    /// <returns>Query</returns>
    public virtual IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter)
    {
        query = dbSet;

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

        return this.query.ToList();
    }

    /// <summary>
    /// Gets one record based on a one-to-one relationship.
    /// </summary>
    /// <param name="filter">Lambda Expression.</param>
    /// <returns>One record.</returns>
    public virtual TEntity GetIt(Expression<Func<TEntity, bool>> filter)
    {
        IQueryable<TEntity> query = dbSet;
        return query.SingleOrDefault(filter);

    }


    #endregion
    #region CRUD Functionality

    /// <summary>
    /// Used to create a new entity into the database.
    /// </summary>
    /// <param name="entity">Entity to create.</param>
    public virtual void Create(TEntity entity)
    {
        dbSet.Add(entity);
    }

    /// <summary>
    /// Used to update an entity that already exists in the
    /// database.
    /// </summary>
    /// <param name="entity">Entity to update.</param>
    public virtual void Update(TEntity entity)
    {
        dbSet.Attach(entity);
        context.Entry(entity).State = EntityState.Modified;
    }

    /// <summary>
    /// Used to delete an entity from the database.
    /// </summary>
    /// <param name="entity">Entity to delete.</param>
    public virtual void Delete(TEntity entity)
    {
        if (context.Entry(entity).State == EntityState.Detached)
        {
            dbSet.Attach(entity);
        }
        dbSet.Remove(entity);
    }

    #endregion
    #endregion

}
#endregion

GenericUnitOfWork.cs:

  /// <summary>
/// Unit of work class that handles multiple Repositories
/// and shares the context.
/// </summary>
public class GenericUnitOfWork : IUnitOfWork

{
    private AccountsContext context = new AccountsContext();

    Dictionary<string, GenericRepository<IRepository<IRepositoryEntity>>> repostories = null;

    /// <summary>
    /// Generic Repository method which checks the repository is available if not,
    /// it sets it up.
    /// </summary>
    /// <param name="entity">Entity</param>
    /// <returns>Repository to use.</returns>
    public  GenericRepository<IRepository<IRepositoryEntity>> Repository (IRepositoryEntity entity)
    {

            string index = entity.GetType().ToString();

            if (!repostories.ContainsKey(index))
            {

                //Reflections to create the repoositiory if it is not needed.
                Type type1 = typeof(GenericRepository<>);
                Type[] typeArgs = {typeof(IRepositoryEntity)};

                Type constructed = type1.MakeGenericType(typeArgs);
                object o = Activator.CreateInstance(constructed);

                if(o is  GenericRepository<IRepository<IRepositoryEntity>>)
                {
                    var rep = (GenericRepository<IRepository<IRepositoryEntity>>)o;
                    rep.context = this.context;
                    repostories.Add(index, rep);  
                }

            }

            return this.repostories[index];
    }

    /// <summary>
    /// Save method.
    /// </summary>
    public void Save()
    {
        context.SaveChanges();

    }

    private bool disposed = false;

    /// <summary>
    /// Dispose the conxtext when finished.
    /// </summary>
    /// <param name="disposing"></param>
    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                context.Dispose();
            }
        }
        this.disposed = true;
    }

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

}

}

Now I know the implementation of repositories Dictionary and the method Repository are incorrect (Generic Types are wrong) because in my lambda expression it cannot resolve the ledgeruser LastName.

var view = unitOfWork.Repository(ledgeruser).Get(l => l.LastName == "smith");

Am I over engineering the issue, or is there a good clean way to create Generic Unit of Work with my collection of Generic Repositories, which are set and created based on the model object give (LedgerUser for e.g. above).

garfbradaz
  • 3,424
  • 7
  • 43
  • 70
  • 1
    Why exactly do you want to create a Repository pattern, and then destroy all the benefits of the repository by doing queries in your controller? What's the point of the repository then if you're just going to treat it like a data context? If you want to do that, then just use the data context, it will be a lot less hassle and you are no worse off (because your app is tightly coupled to the context anyways through the repository). In other words, You're doing a lot of work for no value (I'm not suggesting a repository is bad, it's not, but the way you're using it is) – Erik Funkenbusch Sep 25 '12 at 20:16

2 Answers2

4

A few ideas :

  1. Instead of creating everything with reflection in your UnitOfWork implementation, you could use an IoC container and automate the creation of repositories that way. The MVC framework is actually built for this kind of approach. A simple explanation and example of using StructureMap for this purpose is available at http://www.mikesdotnetting.com/Article/117/Dependency-Injection-and-Inversion-of-Control-with-ASP.NET-MVC .

  2. If you don't want to use a full-blown IoC container, you could still standardize your factory logic and code against the interface. This plays the same kind of role, but may work better if you want to keep using a custom unit of work implementation. In my answer to C#/EF and the Repository Pattern: Where to put the ObjectContext in a solution with multiple repositories?, I posted a RepositoryProvider class that allows the creation of UnitOfWork-scoped repositories with customizeable factories. I would recommend at least looking over this code, as it is a similar, but more effective way to do what your code example aims at. One important thing to understand is that the example in that answer uses an ObjectContext as a UnitOfWork, so your change would need to account for this discrepancy by replacing occurrences of ObjectContext with an occurrence of IUnitOfWork. If anything about that approach is unclear after reviewing the code, let me know and I will try to explain how to adapt your specific use case.

  3. It seems like there is something circular about your factory logic. If the repository creates LedgerUsers, then it shouldn't require a LedgerUser to create the factory. It seems to me like what you really want is a Type parameter like unitOfWork.Repository(typeof(LedgerUser)). You could make this more fluent by creating a generic type parameter over load and do unitOfWork.Repository<LedgerUser>()`. Based on your example, there doesn't seem to be any reason at all that you would need an instance.

  4. It all seems like you would prefer strong-typing to an interface in your Repository method. I think maybe what you are going for is more like:


    public IRepository Repository()
        where T : IRepositoryEntity 
    { 
           //  your factory/cache-retrieval logic here
    }

instead of

public  GenericRepository<IRepository<IRepositoryEntity>> Repository (IRepositoryEntity entity)
{
      //  your factory/cache-retrieval logic here
}

Then, if your call was to Repository<LedgerUser>, your method would return a GenericRepository<LedgerUser>, even though the signature says IRepository<LedgerUser>. This is pretty much how the link to the RepositoryProvider implementation that I suggested works.

Community
  • 1
  • 1
smartcaveman
  • 41,281
  • 29
  • 127
  • 212
  • Thank you @smartcaveman for your speedy and comprehensive, I will update you on my progress later on today. – garfbradaz Sep 26 '12 at 08:25
  • Hey, 4. did work as explained, but i like the sound of 2. so im going to have a good read of the code tonight. Do you mind if i keep the question open? In additon I'm learning DI separately by reading the "DI in .NET" book at the moment. Everyone is screaming at me to use IoC but I will implement that at a later date :) – garfbradaz Sep 26 '12 at 19:30
  • @garfbradaz, You can augment the class linked from #2 with a generic overload like in #4. The key thing for you to get a hold of is the difference between types and instances. It can get a little confusing at first, because a `Type` is an instance whose Type is `Type`. – smartcaveman Sep 26 '12 at 22:46
  • @garfbradaz, also, since you do want to switch to IoC, it will be helpful for you to understand that the `RepositoryProvider` class is basically an IoC container that only contains repositories. A general purpose IoC container works pretty much the same way but can create any Type of configured object and is not limited to those objects that inherit from `IRepository` – smartcaveman Sep 26 '12 at 22:50
  • Apologies what i meant to say was 4 DIDNT work, not DID :P It doesnt matter too much as I'm going to sit down with 2. tonight and give it a good go. The wife is out, the TV will be off and the brain will be in gear! I will probably have lots of questions before the evening is out! Thanks again :) – garfbradaz Sep 27 '12 at 13:33
  • I have marked smartcaveman as the answer and I will update the post once I have implemented the Repository Provider correctly. – garfbradaz Sep 29 '12 at 07:59
2

i do not see why you want to pass an instance to the object instead of the type of the object, and hold repositories by type:

try this

ConcurrentDictionary<Type, object> _repositories = ...;

public GenericRepository<IRepository<TEntity>> Repository<TEntity>(IRepositoryEntity entity) where TEntity: IRepositoryEntity
{
    return (GenericRepository<IRepository<TEntity>>)_repositories.GetOrAdd(
        typeof(TEntity), 
        t => new GenericRepository<IRepository<TEntity>>(this.Context)
    );
}
esskar
  • 10,638
  • 3
  • 36
  • 57
  • 1
    HEy @esskar, thank you for your speedy response. I will be looking at this later on today and i will update everyone then. Thanks again! – garfbradaz Sep 26 '12 at 08:25