POST 调用时未保存工作单元
Unit of Work Not Saving On POST Call
我正在遵循存储库模式并实施 UnitOfWork。
这里是问题所在:
在 EmployeeController
中,有一个名为 AddEmployee()
的 post 调用。此操作方法获取适当的数据并且结果返回成功,但是数据没有保存到数据库。动作方法也在调用 SaveEmployee()
方法,理论上应该保存数据。
Git Repo: https://bitbucket.org/ChaseHardin/myapp
Question: Why isn't the UnitOfWork saving the database changes?
控制器:
[HttpPost]
public HttpResponseMessage AddEmployee([FromBody]Employee employee)
{
if (ModelState.IsValid)
{
_employeeService.AddEmployee(employee);
_employeeService.SaveEmployee();
return new HttpResponseMessage(HttpStatusCode.OK);
}
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
服务:
public class EmployeeService : IEmployeeService
{
private readonly IEmployeeRepository _employeeRepository;
private readonly IUnitOfWork _unitOfWork;
public EmployeeService(IUnitOfWork unitOfWork, IEmployeeRepository employeeRepository)
{
_unitOfWork = unitOfWork;
_employeeRepository = employeeRepository;
}
public Employee GetEmployee(int id)
{
return _employeeRepository.GetById(id);
}
public void SaveEmployee()
{
_unitOfWork.Commit();
}
public void AddEmployee(Employee employee)
{
_employeeRepository.Add(employee);
}
}
public interface IEmployeeService
{
Employee GetEmployee(int id);
void SaveEmployee();
void AddEmployee(Employee employee);
}
UnitOfWork
public class UnitOfWork : IUnitOfWork
{
private readonly IDbFactory dbFactory;
private MyAppEntities dbContext;
public UnitOfWork(IDbFactory dbFactory)
{
this.dbFactory = dbFactory;
}
public MyAppEntities DbContext
{
get { return dbContext ?? (dbContext = dbFactory.Init()); }
}
public void Commit()
{
DbContext.Commit();
}
}
public interface IUnitOfWork
{
void Commit();
}
MyAppEntities:
public class MyAppEntities : DbContext
{
public MyAppEntities() : base("MyAppEntities") { }
public DbSet<Employee> Employees { get; set; }
public virtual void Commit()
{
SaveChanges();
}
}
EmployeeRepository
public class EmployeeService : IEmployeeService
{
private readonly IEmployeeRepository _employeeRepository;
private readonly IUnitOfWork _unitOfWork;
public EmployeeService(IUnitOfWork unitOfWork, IEmployeeRepository employeeRepository)
{
_unitOfWork = unitOfWork;
_employeeRepository = employeeRepository;
}
public Employee GetEmployee(int id)
{
return _employeeRepository.GetById(id);
}
public void SaveEmployee()
{
_unitOfWork.Commit();
}
public void AddEmployee(Employee employee)
{
_employeeRepository.Add(employee);
}
}
public interface IEmployeeService
{
Employee GetEmployee(int id);
void SaveEmployee();
void AddEmployee(Employee employee);
}
基础回购
public abstract class BaseRepository <T> where T : class
{
private MyAppEntities _dataContext;
private readonly IDbSet<T> _dbSet;
protected IDbFactory DbFactory { get; private set; }
protected MyAppEntities DbContext
{
get { return _dataContext ?? (_dataContext = DbFactory.Init()); }
}
protected BaseRepository(IDbFactory dbFactory)
{
DbFactory = dbFactory;
_dbSet = DbContext.Set<T>();
}
#region Implementation
public virtual void Add(T entity)
{
_dbSet.Add(entity);
}
public virtual void Update(T entity)
{
_dbSet.Attach(entity);
_dataContext.Entry(entity).State = EntityState.Modified;
}
public virtual void Delete(T entity)
{
_dbSet.Remove(entity);
}
public virtual void Delete(Expression<Func<T, bool>> where)
{
IEnumerable<T> objects = _dbSet.Where<T>(where).AsEnumerable();
foreach (T obj in objects)
_dbSet.Remove(obj);
}
public virtual T GetById(int id)
{
return _dbSet.Find(id);
}
public virtual IEnumerable<T> GetAll()
{
return _dbSet.ToList();
}
public virtual IEnumerable<T> GetMany(Expression<Func<T, bool>> where)
{
return _dbSet.Where(where).ToList();
}
public T Get(Expression<Func<T, bool>> where)
{
return _dbSet.Where(where).FirstOrDefault<T>();
}
#endregion
}
public interface IBaseRepository<T> where T : class
{
void Add(T entity);
void Update(T entity);
void Delete(T entity);
void Delete(Expression<Func<T, bool>> where);
T GetById(int id);
T Get(Expression<Func<T, bool>> where);
IEnumerable<T> GetAll();
IEnumerable<T> GetMany(Expression<Func<T, bool>> where);
}
问题是您的 IOC 范围。在 App_Start
中,您将使用默认值 Transient Scope 绑定您的对象。这导致创建多个上下文,并且您在一个上下文中添加员工并在另一个上下文中调用 SaveChanges()
。
更新您的 Ninject 绑定以使用 InSingletonScope()
或 InRequestScope()
。
正如@TimS 所提到的,这完全是关于您如何在应用程序启动时注册您的服务。当我使用 autofac 而不是 Ninject 时,我遇到了类似模式的同样问题。
使用 autofac,如果您不选择生命周期范围,则它是 Instance Per Dependency scope as default. By this scope for each request an instance of registered object returns; this cause multiple context at same scope and when you commit your changes by unitofwork instance it just commit wrong instance of Context. To avoiding this you have to use single instance scope or better for this case Instance Per Lifetime Scope
This scope applies to nested lifetimes. A component with per-lifetime
scope will have at most a single instance per nested lifetime scope.
This is useful for objects specific to a single unit of work that may
need to nest additional logical units of work. Each nested lifetime
scope will get a new instance of the registered dependency.
var builder = new ContainerBuilder();
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerLifetimeScope();
builder.RegisterType<DbFactory>().As<IDbFactory>().InstancePerLifetimeScope();
我正在遵循存储库模式并实施 UnitOfWork。
这里是问题所在:
在 EmployeeController
中,有一个名为 AddEmployee()
的 post 调用。此操作方法获取适当的数据并且结果返回成功,但是数据没有保存到数据库。动作方法也在调用 SaveEmployee()
方法,理论上应该保存数据。
Git Repo: https://bitbucket.org/ChaseHardin/myapp
Question: Why isn't the UnitOfWork saving the database changes?
控制器:
[HttpPost]
public HttpResponseMessage AddEmployee([FromBody]Employee employee)
{
if (ModelState.IsValid)
{
_employeeService.AddEmployee(employee);
_employeeService.SaveEmployee();
return new HttpResponseMessage(HttpStatusCode.OK);
}
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
服务:
public class EmployeeService : IEmployeeService
{
private readonly IEmployeeRepository _employeeRepository;
private readonly IUnitOfWork _unitOfWork;
public EmployeeService(IUnitOfWork unitOfWork, IEmployeeRepository employeeRepository)
{
_unitOfWork = unitOfWork;
_employeeRepository = employeeRepository;
}
public Employee GetEmployee(int id)
{
return _employeeRepository.GetById(id);
}
public void SaveEmployee()
{
_unitOfWork.Commit();
}
public void AddEmployee(Employee employee)
{
_employeeRepository.Add(employee);
}
}
public interface IEmployeeService
{
Employee GetEmployee(int id);
void SaveEmployee();
void AddEmployee(Employee employee);
}
UnitOfWork
public class UnitOfWork : IUnitOfWork
{
private readonly IDbFactory dbFactory;
private MyAppEntities dbContext;
public UnitOfWork(IDbFactory dbFactory)
{
this.dbFactory = dbFactory;
}
public MyAppEntities DbContext
{
get { return dbContext ?? (dbContext = dbFactory.Init()); }
}
public void Commit()
{
DbContext.Commit();
}
}
public interface IUnitOfWork
{
void Commit();
}
MyAppEntities:
public class MyAppEntities : DbContext
{
public MyAppEntities() : base("MyAppEntities") { }
public DbSet<Employee> Employees { get; set; }
public virtual void Commit()
{
SaveChanges();
}
}
EmployeeRepository
public class EmployeeService : IEmployeeService
{
private readonly IEmployeeRepository _employeeRepository;
private readonly IUnitOfWork _unitOfWork;
public EmployeeService(IUnitOfWork unitOfWork, IEmployeeRepository employeeRepository)
{
_unitOfWork = unitOfWork;
_employeeRepository = employeeRepository;
}
public Employee GetEmployee(int id)
{
return _employeeRepository.GetById(id);
}
public void SaveEmployee()
{
_unitOfWork.Commit();
}
public void AddEmployee(Employee employee)
{
_employeeRepository.Add(employee);
}
}
public interface IEmployeeService
{
Employee GetEmployee(int id);
void SaveEmployee();
void AddEmployee(Employee employee);
}
基础回购
public abstract class BaseRepository <T> where T : class
{
private MyAppEntities _dataContext;
private readonly IDbSet<T> _dbSet;
protected IDbFactory DbFactory { get; private set; }
protected MyAppEntities DbContext
{
get { return _dataContext ?? (_dataContext = DbFactory.Init()); }
}
protected BaseRepository(IDbFactory dbFactory)
{
DbFactory = dbFactory;
_dbSet = DbContext.Set<T>();
}
#region Implementation
public virtual void Add(T entity)
{
_dbSet.Add(entity);
}
public virtual void Update(T entity)
{
_dbSet.Attach(entity);
_dataContext.Entry(entity).State = EntityState.Modified;
}
public virtual void Delete(T entity)
{
_dbSet.Remove(entity);
}
public virtual void Delete(Expression<Func<T, bool>> where)
{
IEnumerable<T> objects = _dbSet.Where<T>(where).AsEnumerable();
foreach (T obj in objects)
_dbSet.Remove(obj);
}
public virtual T GetById(int id)
{
return _dbSet.Find(id);
}
public virtual IEnumerable<T> GetAll()
{
return _dbSet.ToList();
}
public virtual IEnumerable<T> GetMany(Expression<Func<T, bool>> where)
{
return _dbSet.Where(where).ToList();
}
public T Get(Expression<Func<T, bool>> where)
{
return _dbSet.Where(where).FirstOrDefault<T>();
}
#endregion
}
public interface IBaseRepository<T> where T : class
{
void Add(T entity);
void Update(T entity);
void Delete(T entity);
void Delete(Expression<Func<T, bool>> where);
T GetById(int id);
T Get(Expression<Func<T, bool>> where);
IEnumerable<T> GetAll();
IEnumerable<T> GetMany(Expression<Func<T, bool>> where);
}
问题是您的 IOC 范围。在 App_Start
中,您将使用默认值 Transient Scope 绑定您的对象。这导致创建多个上下文,并且您在一个上下文中添加员工并在另一个上下文中调用 SaveChanges()
。
更新您的 Ninject 绑定以使用 InSingletonScope()
或 InRequestScope()
。
正如@TimS 所提到的,这完全是关于您如何在应用程序启动时注册您的服务。当我使用 autofac 而不是 Ninject 时,我遇到了类似模式的同样问题。
使用 autofac,如果您不选择生命周期范围,则它是 Instance Per Dependency scope as default. By this scope for each request an instance of registered object returns; this cause multiple context at same scope and when you commit your changes by unitofwork instance it just commit wrong instance of Context. To avoiding this you have to use single instance scope or better for this case Instance Per Lifetime Scope
This scope applies to nested lifetimes. A component with per-lifetime scope will have at most a single instance per nested lifetime scope.
This is useful for objects specific to a single unit of work that may need to nest additional logical units of work. Each nested lifetime scope will get a new instance of the registered dependency.
var builder = new ContainerBuilder();
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerLifetimeScope();
builder.RegisterType<DbFactory>().As<IDbFactory>().InstancePerLifetimeScope();