Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I am facing a problem to save data using UnitOfWork. I mean I am unable to save data using UnitOFWork.Commit() from Controller class. My Implementation check bellow.

IUnitOfWork

public interface IUnitOfWork<C> :  IDisposable
{
        int Commit();
        C GetContext { get; set; }
        TransactionScope BeginTransaction();
}

UnitOfWork

public class UnitOfWork<C> : IUnitOfWork<C> where C : DbContext
    {
        private bool _disposed;
        private readonly C _dbContext = null;
        private TransactionScope _transaction;

        public UnitOfWork()
        {
            GetContext = _dbContext ?? Activator.CreateInstance<C>();
        }

        public int Commit()
        {
            return GetContext.SaveChanges();
        }


        public C GetContext
        {
            get;
            set;
        }


        public TransactionScope BeginTransaction()
        {
            if (null != _transaction)
            {
                _transaction = new TransactionScope();
            }

            return _transaction;
        }

        #region IDisposable Members
        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    if (null != _transaction)
                    {
                        _transaction.Dispose();
                    }

                    if (null != _dbContext)
                    {
                        _dbContext.Dispose();
                    }

                }

            }

            _disposed = true;
        }

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


    }

Now within GenericRepository

public abstract class RepositoryBase<C, E> : IRepository<E> 
where E : class 
where C : DbContext
    {
        private readonly IDbSet<E> _dbSet;

        protected RepositoryBase(IUnitOfWork<C> unitOfWork)
        {
            UnitOfWork = unitOfWork;

            _dbSet = UnitOfWork.GetContext.Set<E>();

        }

        protected IUnitOfWork<C> UnitOfWork
        {
            get;
            private set;
        }


        #region IRepository<E> Members

        public void Insert(E entity)
        {
            _dbSet.Add(entity);
            UnitOfWork.GetContext.Entry(entity).State = System.Data.EntityState.Added;
            UnitOfWork.Commit();
        }

[...]

EmployeeRepository

public interface IEmployeeRepository : IRepository<EmployeeModel>
{

}

public class EmployeeRepository : RepositoryBase<MyDbContext, EmployeeModel>, IEmployeeRepository
{
    public EmployeeRepository(IUnitOfWork<MyDbContext> unitOfWork)
        : base(unitOfWork)
    {

    }
}

Note: If I call UnitOfWork.Commit() within GenericRepository described above everything fine. Data Saved successfully. But When I call UnitOfWork.Commit(); from Controller unable to save data. Check code bellow...

GenericRepository Insert Method

public void Insert(E entity)
{
    _dbSet.Add(entity);
    UnitOfWork.GetContext.Entry(entity).State = System.Data.EntityState.Added;

}

Now withing Controller

private readonly IEmployeeRepository _employeeRepository;

    public EmployeeController(IEmployeeRepository employeeRepositoty, IUnitOfWork<MyDbContext> unitOfWork)
    {
        UnitOfWork = unitOfWork;
        this._employeeRepository = employeeRepositoty;
    }

  [HttpPost]
        public ActionResult Create(EmployeeModel employeemodel)
        {
            if (ModelState.IsValid)
            {

                    using (UnitOfWork)
                    {
                        _employeeRepository.Insert(employeemodel);

                        UnitOfWork.Commit();  //OR UnitOfWork.GetContext.SaveChanges();                  

                    }  

                return RedirectToAction("Index");
            }

            return View(employeemodel);
        }

I use Ninject as DI Framework. As a Said I am able to Save data , when I call UnitOfWork.Comit() within GenericRepository.

But when I want to use within Controller then unable to save data using UnitOfWork.commit(),

Di Configuration

 kernel.Bind<IEmployeeRepository>().To<EmployeeRepository>().InRequestScope();

//For Unit Of Work

Kernrl.bind<IUnitOWork<MyDbContext>>.To<UnitOfWork>();

Or

kernel.Bind<IUnitOfWork<MyDbContext>>().To<MyDbContext>().InRequestScope();

Or

kernel.Bind<IUnitOfWork<MyDbContext>>().To<UnitOfWork<MyDbContext>>().InRequestScope();

I try all the above for UnitOfWork. But no luck :( . If I call Commit() from controller
unable to save data. But No error thrown.

So do you feel forcefully create of

GetContext = _dbContext ?? Activator.CreateInstance<C>(); is the problem ?

Because I already declare Kernrl.bind !!!

Please help me unable to understand the problem.


Probably Wrong DI (Ninject) Configuration create the problem. Solution check bellow.

kernel.Bind<IUnitOfWork<MyDbContext>>().To<UnitOfWork<MyDbContext>>().InRequestScope();
kernel.Bind<IEmployeeRepository>().To<EmployeeRepository>().InRequestScope();

Actualy I don't use .InRequestScope(); in my previous configuration. that's why It create mysterious behavior.

But Still unable to understand the reason behind that. Clarification welcome.

According the clarification (Problem: Since you use UnitOfWork both in your repository, and in your controller, you had two different instances, and each instance had its own context. So, you added the entities to one context (the one in your repository) but you called SaveChanges on a different context. Since that different context didn't have anything added to it, nothing happened.

Suggestion: you shouldn't need to make the repository InRequestScope, only the UnitOfWork, since that holds the context). Best solution check bellow.

kernel.Bind<IUnitOfWork<MyDbContext>>().To<UnitOfWork<MyDbContext>>().InRequestScope();
kernel.Bind<IEmployeeRepository>().To<EmployeeRepository>();
share|improve this question
add comment

1 Answer

DbContext itself represents a unit-of-work, no need to add another layer on top of it. Transactions also represent unit-of-work, so inside your IUnitOfWork interface you actually combine 3 different approaches/names for the same pattern.

DbContext represents a repository pattern as well.

So, what you have done is created another layer of abstraction over abstraction without getting real benefits from it. The code like in your GenericRepository Insert Method is already confusing, you're registering a single operation within 2 different concepts that are assumed to communicate to each other somehow.

What I would suggest to improve your code is to hide all the work related to DbContext inside base Controller class where you can prepare a new instance of DbContext (or class derived from it), commit changes if there were no exceptions in action processing and finally dispose it. Then the actual action code would be as simple as

    [HttpPost]
    public ActionResult Create(EmployeeModel employeemodel)
    {
        if (ModelState.IsValid)
        {
            //Db is the instance of class derived from DbContext, initialized in base controller
            Db.Employees.Add(employeemodel);
            return RedirectToAction("Index");
        }

        return View(employeemodel);
    }

In case when no exception has been thrown in this method base controller should commit all the changes stored in Db with Db.SaveChanges()

share|improve this answer
    
+1, ayende.com/blog/4784/… and ayende.com/blog/3955/repository-is-the-new-singleton are more detailed explanations of why building a repository on top of something like EF is mostly pointless. –  Kittoes Feb 14 '13 at 20:35
    
@Kittoes I'm not familiar with EF. If repo pattern is pointless, how do I mock Db and Db.Employees? –  Jean-Philippe Leclerc Feb 4 at 18:40
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.