Save Galley,
I've created a small project to apply IoC using Unity from MS. First of all I've always used the UnitOfWork concept just instantiated it in my controllers , but now I want to inject them. When I start the web application it correctly lists the database information, but whenever I call the SaveChanges it is giving the error: {"The operation can not be completed because the DbContext has been disposed."}
I can not find the reason for this since the context is being injected into UnitOfWork correctly, otherwise it would not even be able to list the bank information. Follow my code.
Unity Config
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Mvc;
using MyMoney.DAL.Repositories;
using MyMoney.DAL.UnitOfWork;
using MyMoney.Domain.Interfaces.Repositories;
using System.Web.Mvc;
namespace MyMoney.DAL.Unity
{
public class Bootstrapper
{
public static IUnityContainer Initialise()
{
var container = BuildUnityContainer();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
return container;
}
private static IUnityContainer BuildUnityContainer()
{
var container = new UnityContainer();
container.RegisterType(typeof(ITransaction), typeof(TransactionRepository));
container.RegisterType<IUnitOfWork, UnitOfWork.UnitOfWork>(new ContainerControlledLifetimeManager());
return container;
}
}
}
UnitOfWork
using System;
using MyMoney.DAL.Context;
using MyMoney.Domain.Interfaces.Repositories;
namespace MyMoney.DAL.UnitOfWork
{
public class UnitOfWork : IUnitOfWork
{
private MyMoneyContext _context;
private ITransaction _transactionRepository;
public UnitOfWork(MyMoneyContext context, ITransaction transactionRepository)
{
this._context = context;
this._transactionRepository = transactionRepository;
}
public ITransaction TransactionRepository
{
get
{
return _transactionRepository;
}
}
public void Save()
{
_context.SaveChanges();
}
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);
}
}
}
Base Repository
using MyMoney.DAL.Context;
using MyMoney.Domain.Interfaces.Repositories;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
namespace MyMoney.DAL.Repositories
{
public abstract class RepositoryBase<TEntity> : IDisposable, IRepositoryBase<TEntity> where TEntity : class
{
protected MyMoneyContext db;
public RepositoryBase(MyMoneyContext db)
{
this.db = db;
}
public virtual void Add(TEntity obj, Guid userId)
{
db.Set<TEntity>().Add(obj);
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
db.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public virtual IEnumerable<TEntity> GetAll(Guid userId)
{
return db.Set<TEntity>().ToList();
}
public virtual TEntity GetById(int id, Guid userId)
{
return db.Set<TEntity>().Find(id);
}
public virtual void Remove(int id)
{
TEntity obj = db.Set<TEntity>().Find(id);
db.Set<TEntity>().Remove(obj);
}
public void Save()
{
db.SaveChanges();
}
public virtual void Update(TEntity obj)
{
db.Entry(obj).State = EntityState.Modified;
}
}
}
Repository
using MyMoney.DAL.Context;
using MyMoney.Domain.Entities;
using MyMoney.Domain.Interfaces.Repositories;
using System;
using System.Collections.Generic;
using System.Linq;
namespace MyMoney.DAL.Repositories
{
public class TransactionRepository : RepositoryBase<Transaction>, ITransaction
{
public TransactionRepository(MyMoneyContext db) : base(db)
{
}
public override IEnumerable<Transaction> GetAll(Guid userId)
{
return db.Transations.Where(x => x.UserId == userId).ToList();
}
public override void Add(Transaction transaction, Guid userId)
{
transaction.UserId = userId;
db.Transations.Add(transaction);
}
public override Transaction GetById(int id, Guid userId)
{
return db.Transations.Where(x => x.UserId == userId && x.Id == id).FirstOrDefault();
}
}
}
Controller
using AutoMapper;
using MyMoney.Domain.Entities;
using MyMoney.MVC.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using Microsoft.AspNet.Identity;
using System.Data.Entity.Validation;
using System.Text;
using MyMoney.DAL.UnitOfWork;
namespace MyMoney.MVC.Controllers
{
public class TransactionsController : Controller
{
readonly IUnitOfWork _unitOfWork;
readonly Guid userId;
public TransactionsController(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
userId = new Guid(System.Web.HttpContext.Current.User.Identity.GetUserId());
}
// GET: Transactions
public ActionResult Index()
{
return View();
}
public ActionResult TransactionList()
{
var tList = Mapper.Map<IEnumerable<Transaction>, IEnumerable<TransactionViewModel>>(_unitOfWork.TransactionRepository.GetAll(userId));
return this.PartialView("TransactionList", tList.ToList());
}
public PartialViewResult EditCreateForm(int id = 0)
{
TransactionViewModel viewModel = null;
if (id > 0)
{
var transaction = _unitOfWork.TransactionRepository.GetById(id, userId);
viewModel = Mapper.Map<Transaction, TransactionViewModel>(transaction);
}
return this.PartialView("EditCreate", viewModel);
}
[HttpPost]
public ActionResult EditCreate(TransactionViewModel viewModel)
{
try
{
if (viewModel.Id > 0)
{
var transactionDomain = Mapper.Map<TransactionViewModel, Transaction>(viewModel);
_unitOfWork.TransactionRepository.Update(transactionDomain);
_unitOfWork.Save();
return Json(0);
}
else
{
var transactionDomain = Mapper.Map<TransactionViewModel, Transaction>(viewModel);
_unitOfWork.TransactionRepository.Add(transactionDomain, userId);
_unitOfWork.Save();
return Json(0);
}
}
catch (Exception ex)
{
var msg = new StringBuilder();
if (ex is DbEntityValidationException)
{
var errors = ex as DbEntityValidationException;
foreach (var err in errors.EntityValidationErrors.SelectMany(x => x.ValidationErrors.Select(get => get.ErrorMessage)))
{
msg.AppendLine(err);
}
}
else
{
msg.AppendLine(ex.Message);
}
return Json(msg.ToString());
}
}
[HttpPost]
public ActionResult DeleteConfirmed(int id)
{
try
{
_unitOfWork.TransactionRepository.Remove(id);
_unitOfWork.Save();
return Json(0);
}
catch (Exception ex)
{
var msg = new StringBuilder();
if (ex is DbEntityValidationException)
{
var errors = ex as DbEntityValidationException;
foreach (var err in errors.EntityValidationErrors.SelectMany(x => x.ValidationErrors.Select(get => get.ErrorMessage)))
{
msg.AppendLine(err);
}
}
else
{
msg.AppendLine(ex.Message);
}
return Json(msg.ToString());
}
}
protected override void Dispose(bool disposing)
{
_unitOfWork.Dispose();
base.Dispose(disposing);
}
}
}
Context
using MyMoney.DAL.EntityCfg;
using MyMoney.Domain.Entities;
using System;
using System.Data.Entity;
using System.Linq;
namespace MyMoney.DAL.Context
{
public class MyMoneyContext : DbContext
{
public MyMoneyContext()
: base("MyMoney")
{
}
public DbSet<Transaction> Transations { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new TransactionConfig());
}
public override int SaveChanges()
{
foreach (var entry in ChangeTracker.Entries().Select(x => x))
{
if (entry.State == EntityState.Added)
{
entry.Property("Date").CurrentValue = DateTime.Now;
}
if (entry.State == EntityState.Modified)
{
entry.Property("Date").IsModified = false;
entry.Property("UserId").IsModified = false;
}
}
return base.SaveChanges();
}
}
}
If you need more information, I'll post it here. Thanks for the help right away.
New Information:
I discovered the reason for the disposed. The context that I use when I call the SaveChanges in the controller is _unitOfWork.Save () , is a different instance of the context that is in the repository. That is, in UnitOfWork an instance of the context is injected and in the repository another instance of the context is injected. The solution might be to call the SaveChanges in the context of the repository, but in my opinion this is wrong, this should be within UnitOfWork itself. Another solution is to inject UnitiOfWork into the repository and thus use the UnitOfWork context , but in the design it looks like this:
UnitOfWork
public UnitOfWork(MyMoneyContext context, ITransaction transactionRepository)
{
this._context = context;
this._transactionRepository = transactionRepository;
}
public MyMoneyContext DbContext
{
get
{
return _context;
}
}
RepositoryBase
protected MyMoneyContext db;
public RepositoryBase(IUnitOfWork db)
{
this.db = db.DbContext;
}
TransactionRepository
public TransactionRepository(IUnitOfWork db) : base(db)
{
}
The problem here is that I create a circular depenna that will cause a stackoverflow . Anyone have a better idea of how to solve?