简体   繁体   中英

Unit of Work and Repository Pattern in MVC controller constructor injection using Unity not doing any changes to database

1.) I am a building new MVC application with 3 tier project architecture having:

  1. Common Project with entities
  2. Business/Service holding interfaces and logic classes and
  3. Data holding repositories, interfaces, DbContext and UnitOfWork classes. I am using Unity Config to register dependencies, DbContext and UnitOfWork .

2.) I created a repository for each table and one generic repository that does basic CRUD operations.

Example Entity residing in Common Project:

public class MenuSecd
{
    [Key, Column(Order = 0)]
    public string prg_module { get; set; }
    [Key, Column(Order = 1)]
    public int prg_numb { get; set; }
    [Key, Column(Order = 2)]
    public string menu_level { get; set; }
}

My generic Entity Logic Interface residing in Business Project:

public interface IEntityLogic<T> : ILogic where T : class
{
    void Create(T entity);
    void Delete(T entity);
    IEnumerable<T> GetAll();
    void Update(T entity);
}

Entity Logic Class:

public abstract class EntityLogic<T> : IEntityLogic<T> where T : class
{
    IUnitOfWork _unitOfWork;
    IGenericRepository<T> _repository;

    public EntityLogic(IUnitOfWork unitOfWork, IGenericRepository<T> repository)
    {
        _unitOfWork = unitOfWork;
        _repository = repository;
    }

    public virtual void Create(T entity)
    {
        if(entity == null)
        {
            throw new ArgumentNullException(nameof(entity));
        }

        _repository.Add(entity);
        _unitOfWork.Commit();
    }
}

Example Business Logic class for the entity defined in Common Project:

public class MenuSecdLogic : EntityLogic<MenuSecd>, IMenuSecdLogic
{
    IUnitOfWork _unitOfWork;
    IMenuSecdRepository _repository;
    public MenuSecdLogic(IUnitOfWork unitOfWork, IMenuSecdRepository repository) : base(unitOfWork, repository)
    {
        _unitOfWork = unitOfWork;
        _repository = repository;
    }

    public List<MenuSecd> GetItems(string usrgrp_id)
    {
        return _repository.GetItems(usrgrp_id);
    }
}

My Generic Repository in Data Project looks like:

public abstract class GenericRepository<T> : IGenericRepository<T> where T : class
{
    protected DbContext _entities;
    protected readonly IDbSet<T> _dbset;
    public GenericRepository(DbContext context)
    {
        _entities = context;
        _dbset = context.Set<T>();
    }
    public virtual T Add(T entity)
    {
        return _dbset.Add(entity);
    }

    public virtual T Delete(T entity)
    {
        return _dbset.Remove(entity);
    }

    public virtual void Edit(T entity)
    {
        _entities.Entry(entity).State = EntityState.Modified;
    }
}

Repository Interface for the same Entity is defined as:

public interface IMenuSecdRepository : IGenericRepository<MenuSecd>
{
    List<MenuSecd> GetItems(string usrgrp_id);
}

Repository class for above mentioned interface is:

public class MenuSecdRepository : GenericRepository<MenuSecd>, IMenuSecdRepository
{
    public MenuSecdRepository(DbContext context) : base(context)
    {

    }

    public List<MenuSecd> GetItems(string usrgrp_id)
    {
        return _dbset.Where(m => m.usrgrp_id == usrgrp_id).ToList();
    }
}

My DbContext looks like:

public class DashboardContext : DbContext
{
    public DashboardContext() : base("Name=DBEntities")
    {

    }

    public DbSet<MenuSecd> menusecd { get; set; }

    public override int SaveChanges()
    {
        var modifiedEntries = ChangeTracker.Entries().Where(x => x.State == EntityState.Added || x.State == EntityState.Modified);
        //future custom implementation like auditing
        return base.SaveChanges();
    }
}

My UnitOfWork looks like:

public sealed class UnitOfWork : IUnitOfWork
{
    private DbContext _dbContext;
    public UnitOfWork(DbContext context)
    {
        _dbContext = context;
    }

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

    //disposes current object
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    //disposes all external resources
    private void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (_dbContext != null)
            {
                _dbContext.Dispose();
                _dbContext = null;
            }
        }
    }
}

My controller:

public class DashController : Controller
{
    private readonly IMenuSecdLogic _menuSecdLogic;
    public DashController(IMenuSecdLogic menuSecdLogic)
    {
         _menuSecdLogic = menuSecdLogic;          
    }
    public void Save()
    {
         var menuSecd = new menuSecd();
         //populate all fields for entity MenuSecd
         _menuSecdLogic.Create(menuSecd);
    }
}

My Unity Config in App_Start looks like :

public static void RegisterTypes(IUnityContainer container)
{
    container.RegisterType<DbContext, DashboardContext>();
    container.RegisterType<IUnitOfWork, UnitOfWork>();
    container.RegisterType(typeof(IGenericRepository<>), typeof(GenericRepository<>));
    container.RegisterType<IMenuSecdLogic, MenuSecdLogic>();
    container.RegisterType<IMenuSecdRepository, MenuSecdRepository>();
}

So when run above project everything builds fine. But when controller calls:

_menuSecdLogic.Create(menuSecd);

It reaches Entity Logic and adds a new entity to _repository at :

_repository.Add(entity);
_unitOfWork.Commit();

But when it hits next line to actually save it to database which is :

return _dbContext.SaveChanges();

in UnitOfWork.cs file. It comes to dashboardContext where it finally have to save it to database. But it does execute :

var modifiedEntries = ChangeTracker.Entries().Where(x => x.State == EntityState.Added || x.State == EntityState.Modified);
    return base.SaveChanges();

But nothing changes in database. There will be no record in database. To test I have added modifiedEntries to see if it is in context or not. By the time control reaches this point I see no modified entries at all. But in EntityLogic.cs it does add a new entity to local entities in repository. I am not sure what is happening with UnitOfWork here. I ran SQL Profiler to see if it is hitting database or not. Interestingly it is not hitting database at all. But if my make following changes to EntityLogic like this:

public virtual void Create(T entity)
{
    if(entity == null)
    {
        throw new ArgumentNullException(nameof(entity));
    }

    _repository.Add(entity);
    _repository.Save();
    //_unitOfWork.Commit();
}

It hits Database and records gets saved fine. But I am not getting why it is neither tracking changes nor hitting database if I use _unitOfWork.Commit() which I want to do. Please help.

It looks like your issue is the scope of your DbContext . Your UnitOfWork and GenericRepository<T> classes are getting different instances.

Not super familiar with Unity, but it looks like you want to use something like this for your DbContext registration:

container.RegisterType<DbContext, DashboadContext>(new PerRequestLifetimeManager());

This will create a single DashboardContext for each request, and your UnitOfWork and GenericRepository<T> classes will be working within the same context.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM