1
Salve galera,
I created a small project to apply Ioc using the Unity MS. First I always used the concept of Unitofwork just instantiated it into 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 is making the mistake: {"The Operation cannot be completed because the Dbcontext has been disposed."}
I can’t find the reason for this, since the context is being injected into Unitofwork correctly, otherwise he wouldn’t even be able to list the bank’s information. Follows 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);
}
}
}
Repository base
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 put it here. Thanks for your help from now on.
New Information:
I figured out why disposed. The context I use when I call Savechanges, in the controller is _unitOfWork.Save(), is a different instance of the context that is in the repository. That is, in Unitofwork One instance of the context is injected and another instance of the context is injected into the repository. The solution may be to call the Savechanges in the context of the repository, but in my opinion this is wrong, this should be within the Unitofwork same. Another solution is to inject the Unitiofwork within the repository and thus use the context of Unitofwork, only that in the design that is would look 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 is that I create a circular depenica that will cause a stackoverflow. Does anyone have a better idea of how to solve?
I think it would be a good idea to show the
RepositoryBase
– Bruno Costa
@Brunocosta, added Repositorybase.
– rhgm
This Unitofwork is a freak. Get rid of it: http://answall.com/questions/51536/quando-usar-entity-framework-com-repository-pattern/80696#80696
– Leonel Sanches da Silva
@Ciganomorrisonmendez, thanks for the link, the information is very interesting, something that should be reflected! But in my case I really want to create more layers of abstraction, so it doesn’t serve as an answer to my question.
– rhgm
It’s not an "answer to your question," it’s a recommendation. There is an insistence on using this, which is an anti-standard, encouraged by some, but it is totally incorrect. Also, your answer does not answer the question in the error you have.
– Leonel Sanches da Silva
What I want most is a better answer than I found. :)
– rhgm