I am having a hard time figuring this out. I have implemented Repository and Unit of Work patterns as a data access abstraction for my database. I am using Dependency Injection to inject them into the controller. The problem is when the controller is disposed, it disposes the repository, but then when I refresh the page, DbContext object inside repository is null (it shouldn't be, I think). The implementation of classes is shown below.
This is the database Repository class:
public class ConferenceCrawlyDbRepository : IConferenceCrawlyDbRepository
{
private ConferenceCrawlyDbContext _context = null;
private static ConferenceCrawlyDbRepository _instance = null;
public IRepository<AcademicDegree> AcademicDegrees { get; private set; }
public IRepository<Conference> Conferences { get; private set; }
public IRepository<ConferenceComment> ConferenceComments { get; private set; }
public IRepository<ConferenceRating> ConferenceRatings { get; private set; }
public IRepository<ConnectionConferenceRecommendation> ConnectionConferenceRecommendation { get; private set; }
public IRepository<Country> Countries { get; private set; }
public IRepository<ImportantDate> ImportantDates { get; private set; }
public IRepository<Topic> Topics { get; private set; }
public IRepository<UserProfile> UserProfiles { get; private set; }
public IRepository<UserProfileSettings> UserProfileSettings { get; private set; }
public IRepository<UsersConnection> UserConnections { get; private set; }
public IRepository<Venue> Venues { get; private set; }
private ConferenceCrawlyDbRepository(ConferenceCrawlyDbContext context)
{
_context = context;
AcademicDegrees = new Repository<AcademicDegree>(context);
Conferences = new Repository<Conference>(context);
ConferenceComments = new Repository<ConferenceComment>(context);
ConferenceRatings = new Repository<ConferenceRating>(context);
ConnectionConferenceRecommendation = new Repository<ConnectionConferenceRecommendation>(context);
Countries = new Repository<Country>(context);
ImportantDates = new Repository<ImportantDate>(context);
Topics = new Repository<Topic>(context);
UserProfiles = new Repository<UserProfile>(context);
UserProfileSettings = new Repository<UserProfileSettings>(context);
UserConnections = new Repository<UsersConnection>(context);
Venues = new Repository<Venue>(context);
}
public static ConferenceCrawlyDbRepository Instance
{
get
{
if (_instance == null)
_instance = new ConferenceCrawlyDbRepository(new ConferenceCrawlyDbContext());
return _instance;
}
}
public bool CommitChanges()
{
try
{
return _context.SaveChanges() > 0;
}
catch
{
// TODO: Add logging
return false;
}
}
public void Dispose()
{
if (_context != null)
_context.Dispose();
}
And this is the generic repository class:
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
private ConferenceCrawlyDbContext _context = null;
private DbSet<TEntity> _dbSet = null;
public Repository(ConferenceCrawlyDbContext context)
{
_context = context;
_dbSet = context.Set<TEntity>();
}
public DbSet<TEntity> DbSet
{
get
{
return _dbSet;
}
}
public bool Add(TEntity entity)
{
try
{
DbSet.Add(entity);
return true;
}
catch (Exception ex)
{
// TODO: Add logging
return false;
}
}
public bool Update(TEntity entity)
{
try
{
var entry = _context.Entry<TEntity>(entity);
DbSet.Attach(entity);
entry.State = EntityState.Modified;
return true;
}
catch(Exception ex)
{
// TODO: Add logging
return false;
}
}
public bool Remove(TEntity entity)
{
try
{
DbSet.Remove(entity);
return true;
}
catch (Exception ex)
{
// TODO: Add logging
return false;
}
}
public IQueryable<TEntity> GetAll()
{
return DbSet;
}
public IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
{
return DbSet.Where(predicate);
}
public TEntity FindById(params object[] keys)
{
return DbSet.Find(keys);
}
public bool Contains(Expression<Func<TEntity, bool>> predicate)
{
return _dbSet.Any(predicate);
}
public bool CommitChanges()
{
try
{
return _context.SaveChanges() > 0;
}
catch
{
// TODO: Add logging
return false;
}
}
public void Dispose()
{
if (_context != null)
_context.Dispose();
}
}
Here is my custom controller factory for dependency injection:
public class CustomControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType != null)
{
if (controllerType.GetConstructor(new Type[] { typeof(IConferenceCrawlyDbRepository) }) != null)
return Activator.CreateInstance(controllerType, new object[] { ConferenceCrawlyDbRepository.Instance }) as Controller;
else if (controllerType.GetConstructor(new Type[] { typeof(IConferenceCrawlyDbRepository), typeof(IMailService) }) != null)
return Activator.CreateInstance(controllerType, new object[] { ConferenceCrawlyDbRepository.Instance,
#if DEBUG
MockMailService.Instance
#else
MailService.Instance
#endif
}) as Controller;
}
return base.GetControllerInstance(requestContext, controllerType);
}
And finally, controller with its action:
public class HomeController : Controller
{
private IConferenceCrawlyDbRepository _dbRepository;
private IMailService _mailService;
public HomeController(IConferenceCrawlyDbRepository dbRepository, IMailService mailService)
{
_dbRepository = dbRepository;
_mailService = mailService;
}
//
// GET: /
public ActionResult Index()
{
var countries = _dbRepository.Countries.GetAll().ToList();
return View(countries);
}
//
// GET: /Home/About
public ActionResult About()
{
return View();
}
//
// GET: /Home/Contact
public ActionResult Contact()
{
return View(new Contact());
}
//
// POST: /Home/Contact
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Contact(Contact contact)
{
if (ModelState.IsValid)
{
if (await _mailService.SendMail(contact.Email, contact.Title,
String.Format("{1}{0}{2}", Environment.NewLine, contact.Message, String.IsNullOrEmpty(contact.Name) ? "Anonimous" : contact.Name)))
{
ViewBag.SuccessMessage = "Comment has been successfully sent.";
return View(new Contact());
}
else
ViewBag.ErrorMessage = "Couldn't send your email. Please try again later";
}
return View(contact);
}
protected override void Dispose(bool disposing)
{
if (_dbRepository != null)
_dbRepository.Dispose();
base.Dispose(disposing);
}
}
The Exception is thrown inside index action when I call the repository, but only after the first time accessing this controller and it says that DbContext object inside repository. I am obviously doing something wrong, but I can't figure it out.
I figure it out after long staring and debugging of code. It's so simple, because I'm disposing context inside ConferenceCrawlyDbRepository I should also set _instance object which is using it to null, and then it will be recreated along with DbContext object. I fell so stupid right now...
Related
I am using generic repository and unit of work in asp.net MVC. when I want to access the context in controller I get this error:
'The operation cannot be completed because the DbContext has been disposed.'
however when I comment this line:
'Database.SetInitializer(new MigrateDatabaseToLatestVersion());'
in Global.asax the error will fix. but as soon as i uncomment that line the error still happening.
here is generic repository:
public class GenericRepository<TEntity> where TEntity : class
{
internal ZarinParse_Context context;
internal DbSet<TEntity> dbSet;
public GenericRepository(ZarinParse_Context context)
{
this.context = context;
this.dbSet = context.Set<TEntity>();
}
public virtual IEnumerable<TEntity> GetWithRawSql(string query, params object[] parameters)
{
return dbSet.SqlQuery(query, parameters).ToList();
}
public virtual IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "")
{
IQueryable<TEntity> query = dbSet;
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}
public virtual TEntity GetByID(object id)
{
return dbSet.Find(id);
}
public virtual void Insert(TEntity entity)
{
dbSet.Add(entity);
}
public virtual void Update(TEntity entity)
{
dbSet.Attach(entity);
context.Entry(entity).State = EntityState.Modified;
}
public virtual void Delete(object id)
{
TEntity entityToDelete = dbSet.Find(id);
Delete(entityToDelete);
}
public virtual void Delete(TEntity entity)
{
if (context.Entry(entity).State == EntityState.Detached)
{
dbSet.Attach(entity);
}
dbSet.Remove(entity);
}
}
and unit of work:
public class UnitOfWork : IDisposable
{
private ZarinParse_Context context;
private RoleRepository roleRepository;
private UserRepository userRepository;
private UserRoleRepository userRoleRepository;
public UnitOfWork()
{
context = new ZarinParse_Context();
}
public RoleRepository RoleRepository
{
get
{
if (this.roleRepository == null)
{
this.roleRepository = new RoleRepository(context);
}
return roleRepository;
}
}
public UserRepository UserRepository
{
get
{
if (this.userRepository == null)
{
this.userRepository = new UserRepository(context);
}
return userRepository;
}
}
public UserRoleRepository UserRoleRepository
{
get
{
if (this.userRoleRepository == null)
{
this.userRoleRepository = new UserRoleRepository(context);
}
return userRoleRepository;
}
}
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);
}
}
RoleRepository Class:
public class RoleRepository : GenericRepository<Role>
{
public RoleRepository(ZarinParse_Context context)
: base(context)
{
}
}
my Context:
public class ZarinParse_Context : DbContext
{
public IDbSet<User> Users { get; set; }
public IDbSet<Role> Roles { get; set; }
public IDbSet<UserRole> UserRoles { get; set; }
public IDbSet<Product> Products { get; set; }
public IDbSet<Tag> Tags { get; set; }
public IDbSet<TagProduct> TagProducts { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new UserConfig());
modelBuilder.Configurations.Add(new RoleConfig());
modelBuilder.Configurations.Add(new UserRoleConfig());
modelBuilder.Configurations.Add(new ProductConfig());
modelBuilder.Configurations.Add(new TagConfig());
modelBuilder.Configurations.Add(new TagProductConfig());
}
}
my Controller:
public class HomeController : Controller
{
UnitOfWork unitOfWork = new UnitOfWork();
public ActionResult Index()
{
var model = new Role() { Name = "User" };
unitOfWork.RoleRepository.Insert(model);
unitOfWork.Save();
return View();
}
protected override void Dispose(bool disposing)
{
unitOfWork.Dispose();
base.Dispose(disposing);
}
}
and Global.asax File:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
Database.SetInitializer(new MigrateDatabaseToLatestVersion<ZarinParse_Context, Configuration>());
}
}
and Configuration :
public class Configuration : DbMigrationsConfiguration<ZarinParse_Context>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
AutomaticMigrationDataLossAllowed = false;
ContextKey = "ZarinParse.DomainClasses.Context.ZarinParse_Context";
}
protected override void Seed(ZarinParse_Context context)
{
InitializeAdmin(context: context,
roleName: "Admin",
userName: "soheil91141121#gmail.com",
password: "stb_91141121",
fullName: "سهیل تقی زاده بنگر",
mobileNumber: "09356336121",
newsEamilEnabled: true);
}
private void InitializeAdmin(ZarinParse_Context context, string roleName, string userName, string password, string fullName, bool newsEamilEnabled, string mobileNumber)
{
//Create Role Admin if it does not exist
var roleExist = context.Roles.Any(p => p.Name == roleName);
if (!roleExist)
{
var role = Role.Create();
role.Name = roleName;
context.Roles.Add(role);
context.SaveChanges();
}
//Create User if it does not exist
var userExist = context.Users.Any(p => p.UserName == userName);
if (!userExist)
{
var user = User.Create();
user.UserName = userName;
user.Email = userName;
user.Password = password.Encrypt();
user.FullName = fullName;
user.NewsEmailEnabled = newsEamilEnabled;
user.MobileNumber = mobileNumber;
context.Users.Add(user);
context.SaveChanges();
}
//Create UserRole if it does not exist
var role2 = context.Roles.Single(p => p.Name == roleName);
var user2 = context.Users.Single(p => p.UserName == userName);
var userRoleExist = context.UserRoles.Any(p => p.RoleId == role2.RoleId && p.UserId == user2.UserId);
if (!userRoleExist)
{
var userRole = UserRole.Create();
userRole.Role = role2;
userRole.User = user2;
context.UserRoles.Add(userRole);
context.SaveChanges();
}
context.Dispose();
}
}
The last line of InitializeAdmin disposes a context that is passed into the method.
Only dispose of objects that you have created, since you own the object's lifetime.
In other words, don't dispose of objects you don't own.
I am getting this rare exception when I am trying to update an entity. I am working with ASP.NET MVC 5, EF 6.1, Identity and the patterns repository and unit of work.
I followed this cool tutorial from Tim.
I have all the work done in 4 separate projects (UI, Entities, Domain and DataAccess), I included Tim solution to use Users and roles.
My Entities:
public class Turno : IEntity
{
public Turno()
{
//Medico = new Medico();
//Consultorio = new Consultorio();
}
public Int64 Id { get; set; }
[Required]
public DateTime Fecha { get; set; }
[Required]
[StringLength(10)]
public string Hora { get; set; }
[Required]
public Medico Medico { get; set; }
[Required]
public Consultorio Consultorio { get; set; }
[Required]
public EstadoTurno Estado { get; set; }
public virtual Paciente Paciente { get; set; }
}
namespace TurnosMedicos.Entities
{
[Table("Medicos")]
public class Medico: Persona
{
public Medico()
{
Especialidades = new List<Especialidad>();
TurnosDisponibles = new List<Turno>();
Consultorios = new List<Consultorio>();
Telefonos = new List<Telefono>();
PlanesAtendidos = new List<PrepagaPlan>();
}
[Required]
[StringLength(10)]
public string Matricula { get; set; }
[Required]
public decimal PrecioTurno { get; set; }
public virtual List<Especialidad> Especialidades { get; set; }
public virtual List<Turno> TurnosDisponibles { get; set; }
public virtual List<Consultorio> Consultorios { get; set; }
public List<PrepagaPlan> PlanesAtendidos { get; set; }
public override string ToString()
{
return Apellido + ", " + Nombres;
}
}
}
namespace TurnosMedicos.Entities
{
public class Paciente: Persona
{
public Paciente()
{
HistoriaClinica = new List<Consulta>();
Turnos = new List<Turno>();
Telefonos = new List<Telefono>();
PlanesSalud = new List<PrepagaPlan>();
TurnosAusentes = new List<TurnoInformadoAusente>();
}
public virtual List<TurnoInformadoAusente> TurnosAusentes { get; set; }
public virtual List<Consulta> HistoriaClinica { get; set; }
public virtual List<Turno> Turnos { get; set; }
public override string ToString()
{
return Apellido + ", " + Nombres;
}
public List<PrepagaPlan> PlanesSalud { get; set; }
public PrepagaPlan PlanPredeterminado()
{
if(PlanesSalud.Count()>0)
{
return PlanesSalud[0];
}
return null;
}
public string TelefonosRapido()
{
System.Text.StringBuilder tel = new System.Text.StringBuilder();
foreach(var t in this.Telefonos)
{
tel.Append(t.Numero + " (" + t.Tipo + ")");
tel.AppendLine();
}
return tel.ToString();
}
/// <summary>
/// Porcentaje de Asistencia
/// </summary>
[NotMapped]
public decimal Ranking
{
get{
if (TurnosAusentes.Count == 0)
{
return 100;
}
else{
return (100 - (Decimal.Divide(TurnosAusentes.Count, Turnos.Count) * 100));
}
}
}
}
}
My Repositories:
public class MedicosRepository: Repository<Medico> //, IMedicoRepository
{
internal MedicosRepository(ApplicationDbContext context)
: base(context)
{
}
public IQueryable<Medico> Find(System.Linq.Expressions.Expression<Func<Medico, bool>> predicate)
{
return Set.Where(predicate);
}
public override List<Medico> GetAll()
{
return Set.Include("Telefonos")
.Include("PlanesAtendidos")
.Include("Consultorios")
.Include("Consultorios.Telefonos")
.Include("TurnosDisponibles.Paciente.Telefonos")
.ToList();
}
public IQueryable<Medico> FindAll()
{
return Set.Include("Telefonos")
.Include("PlanesAtendidos")
.Include("Consultorios")
.Include("Consultorios.Telefonos")
.Include("TurnosDisponibles.Paciente.Telefonos");
}
public override Medico FindById(object id)
{
Int64 Id = Int64.Parse(id.ToString());
return Set.Include("Telefonos")
.Include("Consultorios")
.Include("Consultorios.Telefonos")
.Include("PlanesAtendidos")
.Include("TurnosDisponibles.Paciente.Telefonos")
.Single(o => o.Id == Id);
}
}
TurnosRepository:
namespace TurnosMedicos.DataAccess.Repositories
{
internal class TurnosRepository: Repository<Turno>
{
public TurnosRepository(ApplicationDbContext context): base(context)
{
}
public override List<Turno> GetAll()
{
return Set.Include("Medico")
.Include("Paciente")
.Include("Consultorio").ToList();
}
public override Turno FindById(object id)
{
Int64 Id = Int64.Parse(id.ToString());
return Set.Include("Medico")
.Include("Paciente")
.Include("Consultorio")
.Single(o => o.Id == Id);
}
}
}
User repository:
namespace TurnosMedicos.DataAccess.Repositories
{
internal class UserRepository : Repository<User>, IUserRepository
{
internal UserRepository(ApplicationDbContext context)
: base(context)
{
}
public User FindByUserName(string userName)
{
return Set.Include("Medico")
.Include("Paciente")
.FirstOrDefault(x => x.UserName == userName);
}
public Task<User> FindByUserNameAsync(string userName)
{
return Set.FirstOrDefaultAsync(x => x.UserName == userName);
}
public Task<User> FindByEmailAsync(System.Threading.CancellationToken cancellationToken, string email)
{
return Set.FirstOrDefaultAsync(x => x.Email == email, cancellationToken);
}
public User FindByEmail(string email)
{
return Set.Include("Medico")
.Include("Medico.Telefonos")
.Include("Medico.Especialidades")
.Include("Medico.TurnosDisponibles")
.Include("Medico.Consultorios")
.Include("Medico.PlanesAtendidos")
.Include("Paciente")
.Include("Paciente.Turnos")
.Include("Paciente.PlanesSalud")
.Include("Paciente.HistoriaClinica")
.Include("Paciente.TurnosAusentes")
.Include("Paciente.Telefonos")
.Include("Roles")
.FirstOrDefault(u => u.Email == email);
}
}
}
My Context:
namespace TurnosMedicos.DataAccess
{
internal class ApplicationDbContext : DbContext
{
internal ApplicationDbContext(string nameOrConnectionString)
: base(nameOrConnectionString)
{
}
public ApplicationDbContext(): base("TurnosMedicosCn")
{
}
public IDbSet<User> Users { get; set; }
public IDbSet<Role> Roles { get; set; }
public IDbSet<ExternalLogin> Logins { get; set; }
public DbSet<Paciente> Pacientes { get; set; }
public DbSet<Medico> Medicos { get; set; }
public DbSet<Turno> Turnos { get; set; }
public DbSet<Consulta> Consultas { get; set; }
public DbSet<Consultorio> Consultorios { get; set; }
public DbSet<Especialidad> Especialidades { get; set; }
public DbSet<Prepaga> Prepagas { get; set; }
public DbSet<PrepagaPlan> Planes { get; set; }
public DbSet<Registro> RegistrosFacturacion { get; set; }
public DbSet<Empresa> Empresas { get; set; }
public DbSet<Recomendado> Recomendados { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new UserConfiguration());
modelBuilder.Configurations.Add(new RoleConfiguration());
modelBuilder.Configurations.Add(new ExternalLoginConfiguration());
modelBuilder.Configurations.Add(new ClaimConfiguration());
}
}
}
My Unit Of Work
namespace TurnosMedicos.DataAccess
{
public class SQLUnitOfWork: IUnitOfWork
{
private readonly ApplicationDbContext _context;
PacientesRepository _pacientes = null;
//MedicosRepository _medicos = null;
IRepository<Medico> _medicos = null;
TurnosRepository _turnos = null;
EspecialidadRepository _especialidades = null;
PrepagasRepository _prepagas = null;
PrepagaPlanesRepository _planes = null;
RegistrosFacturacionRepository _registroFacturacion = null;
RecomendadosRepository _recomendados = null;
private IExternalLoginRepository _externalLoginRepository;
private IRoleRepository _roleRepository;
private IUserRepository _userRepository;
public SQLUnitOfWork(string nameOrConnectionString)
{
_context = new ApplicationDbContext(nameOrConnectionString);
}
public IRepository<Turno> Turnos
{
get { return _turnos ?? (_turnos = new TurnosRepository(_context)); }
}
public IRepository<Paciente> Pacientes
{
get
{
if(_pacientes==null)
{
_pacientes = new PacientesRepository(_context);
}
return _pacientes;
}
}
public IRepository<Medico> Medicos
{
get { return _medicos ?? (_medicos = new MedicosRepository(_context)); }
}
public IRepository<Especialidad> Especialidades
{
get
{
if (_especialidades == null)
{
_especialidades = new EspecialidadRepository(_context);
}
return _especialidades;
}
set
{
throw new NotImplementedException();
}
}
public IRepository<Prepaga> Prepagas {
get {
if (_prepagas == null)
{
_prepagas = new PrepagasRepository(_context);
}
return _prepagas;
}
set { throw new NotImplementedException(); }
}
public IRepository<PrepagaPlan> Planes {
get
{
if (_planes == null)
{
_planes = new PrepagaPlanesRepository(_context);
}
return _planes;
}
}
public IRepository<Registro> RegistrosFacturacion
{
get
{
if(_registroFacturacion == null)
{
_registroFacturacion = new RegistrosFacturacionRepository(_context);
}
return _registroFacturacion;
}
}
public IRepository<Recomendado> Recomendados
{
get
{
if (_recomendados == null)
{
_recomendados = new RecomendadosRepository(_context);
}
return _recomendados;
}
}
public IExternalLoginRepository ExternalLoginRepository
{
get { return _externalLoginRepository ?? (_externalLoginRepository = new ExternalLoginRepository(_context)); }
}
public IRoleRepository RoleRepository
{
get { return _roleRepository ?? (_roleRepository = new RoleRepository(_context)); }
}
public IUserRepository UserRepository
{
get { return _userRepository ?? (_userRepository = new UserRepository(_context)); }
}
public int SaveChanges()
{
try
{
return _context.SaveChanges();
}
catch (System.Data.Entity.Validation.DbEntityValidationException dbEx)
{
Exception raise = dbEx;
foreach (var validationErrors in dbEx.EntityValidationErrors)
{
foreach (var validationError in validationErrors.ValidationErrors)
{
string message = string.Format("{0}:{1}",
validationErrors.Entry.Entity.ToString(),
validationError.ErrorMessage);
raise = new InvalidOperationException(message, raise);
}
}
throw raise;
}
}
public Task<int> SaveChangesAsync()
{
try
{
return _context.SaveChangesAsync();
}
catch (System.Data.Entity.Validation.DbEntityValidationException dbEx)
{
Exception raise = dbEx;
foreach (var validationErrors in dbEx.EntityValidationErrors)
{
foreach (var validationError in validationErrors.ValidationErrors)
{
string message = string.Format("{0}:{1}",
validationErrors.Entry.Entity.ToString(),
validationError.ErrorMessage);
raise = new InvalidOperationException(message, raise);
}
}
throw raise;
}
}
public Task<int> SaveChangesAsync(System.Threading.CancellationToken cancellationToken)
{
return _context.SaveChangesAsync(cancellationToken);
}
public void Dispose()
{
_externalLoginRepository = null;
_roleRepository = null;
_userRepository = null;
_turnos = null;
_pacientes = null;
_especialidades = null;
_planes = null;
_prepagas = null;
_recomendados = null;
_registroFacturacion = null;
}
}
}
And Finally, the Domain class where I am trying to get things done
IUnitOfWork unitofwork = null;
IRepository<Paciente> repositoryPacientes = null;
//IMedicoRepository repositoryMedicos = null;
IRepository<Medico> repositoryMedicos = null;
IRepository<Turno> repositoryTurnos = null;
IRepository<Especialidad> repositoryEspecialidad = null;
IRepository<Registro> repositoryRegistroFacturacion = null;
IRepository<Recomendado> repositoryRecomendados = null;
public TurnosManager(IUnitOfWork _unitOfWork)
{
unitofwork = _unitOfWork;
repositoryPacientes = unitofwork.Pacientes;
repositoryMedicos = unitofwork.Medicos;
repositoryTurnos = unitofwork.Turnos;
repositoryEspecialidad = unitofwork.Especialidades;
repositoryRegistroFacturacion = unitofwork.RegistrosFacturacion;
repositoryRecomendados = unitofwork.Recomendados;
}
private bool AsignarTurno(Paciente p, Turno t)
{
if(t.Fecha.Date < DateTime.Now.Date)
{
throw new TurnoInvalidoException("No se puede seleccionar un turno para una fecha en el pasado.");
}
// Ver tema de la hora para la fecha actual
t.Estado = EstadoTurno.Reservado;
t.Paciente = p;
p.Turnos.Add(t);
//repositoryTurnos.Update(t);
unitofwork.SaveChanges();
string planMedico = "Privado";
if (p.PlanesSalud.Count > 0)
{
planMedico = p.PlanesSalud[0].NombrePlan;
}
RegisstrarParaFacturacion(t, planMedico);
ReservaVM obj = new ReservaVM();
obj.Paciente = p;
obj.MedicoSeleccionado = t.Medico;
obj.TurnoSeleccionado = t;
EnviarEmail(obj);
return true;
}
The problem occurs in line unitofwork.SaveChanges();
I get the exception: "You can not define the relationship between two objects because they are associated with different ObjectContext objects" but I only have "ApplicationDbContext" in my code.
The problem is that you have one type which is ApplicationDbContext, but there are two instances of that context. One instantiated to fetch Pacient p and another one for Turno t.
Where is the AsignarTurno(Paciente p, Turno t) called from?
If this is code first you should add
public int PacienteId { get; set; }
to class Turno.
If your classes are well define you should only need to persist the relation on one side, so in the AsignarTurno function instead of:
t.Paciente = p;
p.Turnos.Add(t);
you should only have
t.PacienteId = p.Id;
This would do what you need, assuming that p and t both exist the database.
In my project I don't use a physical delete, I just use logical "soft delete" for all tables.
I use this clause for all queries:
.Where(row => row.IsDeleted == false)
I want a way to avoid to repeat this where clause in all queries.
I have method like this to get data :
public new IDbSet<TEntity> Set<TEntity>() where TEntity : class
{
return base.Set<TEntity>();
}
and I call it like this :
_categories = _uow.Set<Category>();
How can I do this ?
First Idea:
Add a base class and put Deleted column in that and inherit all classes from this base class. Is this a good way?
I use UnitOfWork and code-first.
I would not be creating a base class only for the sake of reuse a single property. Instead, I would create an interfase and an extension method to encapsulate and reuse the where statement. Something like the following:
public static class EntityFrameworkExtentions
{
public static ObservableCollection<TEntity> Alive<TEntity>(this DbSet<TEntity> set)
where TEntity : class, ISoftDeleteAware
{
var data = set.Where(e => e.IsDeleted == false);
return new ObservableCollection<TEntity>(data);
}
}
The interface declaration
public interface ISoftDeleteAware
{
bool IsDeleted { get;set;}
}
Usage:
var coll = DbContext.Categories.Alive();
public class GenericRepository<TEntity> where TEntity : class
{
internal MyContext context;
internal DbSet<TEntity> dbSet;
public GenericRepository(MyContext context)
{
this.context = context;
this.dbSet = context.Set<TEntity>();
}
public virtual IQueryable<TEntity> GetNonDeleted(Expression<Func<TEntity, bool>> filter = null)
{
IQueryable<TEntity> query = dbSet;
if (filter != null)
{
query = query.Where(filter);
}
query = query.Where(row => row.IsDeleted == false);
return query;
}
// Other methods
}
If you never delete anything, then you can use base entity class and IsDelete property with it. After, use this base entity for Generic Repository. It looks like;
public abstract class BaseModel
{
public BaseModel()
{
IsDelete = false;
CreateDate = DateTime.Now;
}
[Key]
public int Id { get; set; }
public bool IsDelete{ get; set; }
public virtual DateTime CreateDate { get; set; }
public virtual DateTime UpdateDate { get; set; }
}
public class YourClassHere : BaseModel
{
//
}
public class Repository<T> : IRepository<T> where T : BaseModel
{
private readonly IDbContext _context;
private IDbSet<T> _entities;
public Repository(IDbContext context)
{
this._context = context;
}
public T GetByIdByIgnoringDeleteStatus(int id)
{
return this.Entities.Find(id);
}
public T GetById(int id)
{
return this.Entities.Single(item => item.Id == id && !item.IsDelete);
}
public void Create(T entity)
{
try
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
entity.CreateDate = DateTime.Now;
this.Entities.Add(entity);
//this._context.SaveChanges();
}
catch (DbEntityValidationException dbEx)
{
var msg = string.Empty;
foreach (var validationErrors in dbEx.EntityValidationErrors)
{
foreach (var validationError in validationErrors.ValidationErrors)
{
msg += string.Format("Property: {0} Error: {1}",
validationError.PropertyName, validationError.ErrorMessage) + Environment.NewLine;
}
}
var fail = new Exception(msg, dbEx);
throw fail;
}
}
public void Update(T entity)
{
try
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
entity.UpdateDate = DateTime.Now;
this._context.SetModified(entity);
//this._context.SaveChanges();
}
catch (DbEntityValidationException dbEx)
{
var msg = string.Empty;
foreach (var validationErrors in dbEx.EntityValidationErrors)
{
foreach (var validationError in validationErrors.ValidationErrors)
{
msg += Environment.NewLine + string.Format("Property: {0} Error: {1}",
validationError.PropertyName, validationError.ErrorMessage);
}
}
var fail = new Exception(msg, dbEx);
throw fail;
}
}
public void Delete(T entity)
{
try
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
entity.UpdateDate = DateTime.Now;
this.Entities.Remove(entity);
//this._context.SaveChanges();
}
catch (DbEntityValidationException dbEx)
{
var msg = string.Empty;
foreach (var validationErrors in dbEx.EntityValidationErrors)
{
foreach (var validationError in validationErrors.ValidationErrors)
{
msg += Environment.NewLine + string.Format("Property: {0} Error: {1}",
validationError.PropertyName, validationError.ErrorMessage);
}
}
var fail = new Exception(msg, dbEx);
throw fail;
}
}
public virtual IQueryable<T> GetAll()
{
return this.Entities;
}
private IDbSet<T> Entities
{
get
{
if (_entities == null)
{
_entities = _context.Set<T>();
}
return _entities;
}
}
}
Now, you can use your class methods with Delete Status.
I'm new to MVC3 and have been following the awesome tutorials on the asp.net website. However, I can't quite wrap my head around how to use Unit of Work and Generic Repository patterns with Ninject. I used this tutorial as a starting point: 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
Without using interfaces, I know I can implement it like so:
Generic Repository:
public class GenericRepository<TEntity> : IGenericRepository<TEntity>
where TEntity : class
{
internal MyContext context;
internal DbSet<TEntity> dbSet;
public GenericRepository(MyContext context)
{
this.context = context;
this.dbSet = context.Set<TEntity>();
}
}
Unit of Work:
private MyContext context = new MyContext();
private GenericRepository<Student> studentRepository;
private GenericRepository<Course> courseRepository;
public GenericRepository<Student> StudentRepository
{
if (this.studentRepository == null)
{
this.studentRepository = new GenericRepository<Student>(context);
}
return studentRepository;
}
public GenericRepository<Course> CourseRepository
{
if (this.courseRepository == null)
{
this.courseRepository = new GenericRepository<Course>(context);
}
return courseRepository;
}
This setup allows me to pass the same context to all repositories, and then call a single Save() function to commit the changes.
I know I can use an interface IGenericRepository<TEntity> and the concrete implementation GenericRepository<TEntity> and then bind them using Ninject:
kernel.Bind(typeof(IGenericRepository<>)).To(typeof(GenericRepository<>));
But how would I go about setting up my IUnitOfWork and UnitOfWork to ensure that all my repositories share a single database context? Am I even doing it right in the first place? I've searched around but all I seem to find are tutorials that only use generic repositories without a unit of work.
Your base Repo:
public class BaseRepository<TObject> : IRepository<TObject>
where TObject : class
{
public BaseRepository(IUnitOfWork unitOfWork)
{
if (unitOfWork == null) throw new ArgumentException("unitOfWork");
UnitOfWork = unitOfWork;
}
protected DbSet<TObject> DbSet
{
get
{
return Context.Set<TObject>();
}
}
public void Dispose()
{
if (sharedContext && (Context != null))
Context.Dispose();
}
public virtual IQueryable<TObject> All()
{
return DbSet.AsQueryable();
}
public virtual IQueryable<TObject>
Filter(Expression<Func<TObject, bool>> predicate)
{
return DbSet.Where(predicate).AsQueryable<TObject>();
}
public virtual IQueryable<TObject> Filter<Key>(Expression<Func<TObject, Key>> sortingSelector, Expression<Func<TObject, bool>> filter, out int total,
SortingOrders sortby = SortingOrders.Asc, int index = 0, int size = 50)
{
int skipCount = index * size;
var _resultSet = filter != null ? DbSet.Where(filter).AsQueryable() : DbSet.AsQueryable();
total = _resultSet.Count();
_resultSet = sortby == SortingOrders.Asc ? _resultSet.OrderBy(sortingSelector).AsQueryable() : _resultSet.OrderByDescending(sortingSelector).AsQueryable();
_resultSet = skipCount == 0 ? _resultSet.Take(size) : _resultSet.Skip(skipCount).Take(size);
return _resultSet;
}
public bool Contains(Expression<Func<TObject, bool>> predicate)
{
return DbSet.Count(predicate) > 0;
}
public virtual TObject Find(params object[] keys)
{
return DbSet.Find(keys);
}
public virtual TObject Find(Expression<Func<TObject, bool>> predicate)
{
return DbSet.FirstOrDefault(predicate);
}
public virtual TObject Create(TObject TObject, bool SaveChanges = true)
{
var newEntry = DbSet.Add(TObject);
if (!sharedContext && SaveChanges)
Context.SaveChanges();
return newEntry;
}
public virtual int Count
{
get
{
return DbSet.Count();
}
}
public virtual int Delete(TObject TObject)
{
DbSet.Remove(TObject);
if (!sharedContext)
return Context.SaveChanges();
return 0;
}
public virtual int Update(TObject TObject, bool SaveChanges = true)
{
var entry = Context.Entry(TObject);
DbSet.Attach(TObject);
entry.State = EntityState.Modified;
if (!sharedContext && SaveChanges)
return Context.SaveChanges();
return 0;
}
public virtual int Delete(Expression<Func<TObject, bool>> predicate)
{
var objects = Filter(predicate);
foreach (var obj in objects)
DbSet.Remove(obj);
if (!sharedContext)
return Context.SaveChanges();
return 0;
}
/// <summary>
/// Sets the state of an entity.
/// </summary>
/// <param name="entity">object to set state.</param>
/// <param name="entityState"><see cref="EntityState"/></param>
protected virtual void SetEntityState(object entity, EntityState entityState)
{
Context.Entry(entity).State = entityState;
}
/// <summary>
///
/// </summary>
/// <param name="entity"></param>
protected virtual void Attach(object entity)
{
if (Context.Entry(entity).State == EntityState.Detached)
Context.Entry(entity).State = EntityState.Modified;
}
protected virtual void Detach(object entity)
{
Context.Entry(entity).State = EntityState.Detached;
}
public void SubmitChanges()
{
UnitOfWork.SaveChanges();
}
#region Properties
private bool sharedContext { get; set; }
/// <summary>
/// Unit of work controlling this repository.
/// </summary>
protected IUnitOfWork UnitOfWork { get; set; }
/// <summary>
/// Provides access to the ef context we are working with
/// </summary>
internal IMyContext Context
{
get
{
return (IMyContext)UnitOfWork;
}
}
#endregion
}
Notice Context is an interface implementing UnitOfWork.
Your context interface:
public interface IMyContext : IDbContext
{
DbSet<Sometype> SomeProperty { get; set; }
...
}
Your IDbContext interface:
public interface IDbContext
{
DbChangeTracker ChangeTracker { get; }
DbContextConfiguration Configuration { get; }
Database Database { get; }
void Dispose();
void Dispose(bool disposing);
DbEntityEntry Entry(object entity);
DbEntityEntry<TEntity> Entry<TEntity>(TEntity entity) where TEntity : class;
bool Equals(object obj);
int GetHashCode();
Type GetType();
IEnumerable<DbEntityValidationResult> GetValidationErrors();
void OnModelCreating(DbModelBuilder modelBuilder);
int SaveChanges();
DbSet<TEntity> Set<TEntity>() where TEntity : class;
DbSet Set(Type entityType);
bool ShouldValidateEntity(DbEntityEntry entityEntry);
string ToString();
DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items);
}
Then the actual context implementation:
public class MyContext : DbContext, IUnitOfWork, IMyContext
{
//public MyContext()
//{
// Database.SetInitializer<ReconContext>(null);
//}
public ReconContext()
: base("Name=ReconContext")
{
((IObjectContextAdapter)this).ObjectContext.ContextOptions.ProxyCreationEnabled = false;
}
public DbSet<SomeType> SomeProperty { get; set; }
....
public new void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new SomePropertyMap());
.....
base.OnModelCreating(modelBuilder);
}
int IUnitOfWork.SaveChanges()
{
return base.SaveChanges();
}
void IDisposable.Dispose()
{
base.Dispose();
}
public new void Dispose(bool disposing)
{
base.Dispose(disposing);
}
public new bool ShouldValidateEntity(DbEntityEntry entityEntry)
{
return base.ShouldValidateEntity(entityEntry);
}
public new DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
return base.ValidateEntity(entityEntry, items);
}
}
Then in your ninject config you simply do:
kernel.Bind<IUnitOfWork<MyContext>>().To<MyContext>().InRequestScope();
I have an asp.net mvc application where I use EF CodeFirst with a repository pattern and am trying to test and catch a DbUpdateConcurrencyException, but it doesn't appear to be firing, evening though when I update a record in one browser, while it is already open in another, and then go to the other and try to update the same record, it allows me to do it. I have a TimeStamp column on the table (called RowVersion) which does change with every update, so that is correct, and from watching sql profiler I can see the update statement does check that the timestamp is what is being passed in, so I'm out of ideas, and am unable to find anything so far to really help me. If anyone can see something I am doing wrong, please let me know. Thanks!!
Here is my entity:
public class MyEntity : EntityBase
{
public override int Id { get; set; }
public DateTime? LastEdited { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
}
Here is the repository and it's base classes/dbcontext:
public class MyEntityDataProvider : EfDataProviderBase<MyEntity>, IMyEntityDataProvider
{
public MyEntityDataProvider (IDataContext dataContext) : base(dataContext) { }
}
public abstract class EfDataProviderBase<T> : DataProviderBase<T> where T : EntityBase
{
public new DbDataContext DataContext
{
get { return base.DataContext as DbDataContext; }
protected set { base.DataContext = value; }
}
public EfDataProviderBase(IDataContext dataContext)
: base(dataContext)
{
if (!(dataContext is DbDataContext)) throw new ArgumentException("Parameter 'dataContext' must be of type DbDataContext.");
}
public override T GetById(int id)
{
if (id < 1) return null;
return this.DataContext.GetDbSet<T>().Find(id);
}
public override List<T> GetAll()
{
return this.DataContext.GetDbSet<T>().OrderBy(e => e.Id).ToList();
}
public override void Insert(T entity)
{
if (entity == null) throw new ArgumentNullException("entity");
this.DataContext.GetDbSet<T>().Add(entity);
}
public override void Update(T entity)
{
if (entity == null) throw new ArgumentNullException("entity");
if (!this.DataContext.GetDbSet<T>().Local.Any(e => e.Id == entity.Id))
this.DataContext.GetDbSet<T>().Attach(entity);
this.DataContext.Entry(entity).State = EntityState.Modified;
}
}
public class DbDataContext : DbContext, IDataContext
{
public DbSet<MyEntity> MyEntities { get; set; }
public DbSet<T> GetDbSet<T>() where T : EntityBase
{
Type entityType = typeof(T);
if (entityType == typeof(User))
return this.Users as DbSet<T>;
}
public void Commit()
{
this.SaveChanges();
}
}
public abstract class DataProviderBase<T> : IDataProvider<T> where T: EntityBase
{
public IDataContext DataContext { get; protected set; }
public DataProviderBase(IDataContext dataContext)
{
if (dataContext == null) throw new ArgumentNullException("dataContext");
this.DataContext = dataContext;
}
public abstract T GetById(int id);
public abstract List<T> GetAll();
public void Save(T entity)
{
if (entity == null) throw new ArgumentNullException("entity");
if (entity.IsNew)
{
this.Insert(entity);
}
else if (entity.IsDeleted)
{
this.Delete(entity);
}
else
{
this.Update(entity);
}
}
public abstract void Insert(T entity);
public abstract void Update(T entity);
}
Here is my controller action:
[HttpPost]
[ValidateAntiForgeryToken()]
public ActionResult UpdateViewModel(ViewModel model)
{
if (ModelState.IsValid)
{
try
{
MyEntity current = this.MyEntityDataProvider.GetById(model.Id);
current.LastEdited = DateTime.UtcNow;
current.RowVersion = model.Timestamp; // should have original timestamp, getting from hidden field on view
this.MyEntityDataProvider.Save(current);
this.DataContext.Commit(); // one dbcontext per http request, injected by ninject
return Json(new { success = "Saved!" });
}
catch (DbUpdateConcurrencyException)
{
ModelState.AddModelError("", "This record has been edited by an another person since you have opened it - please close and re-open to attempt your changes again.");
}
}
return Json(new { errors = GetErrorsFromModelState() });
}
Good morning friend.
You should add information on the property you want to control concurrency.
Example:
[ConcurrencyCheck]
public int Code {get; set;}