我没有使用IoC / DI,但我将来会重构我的代码,所以:
我的代码:
IUnitOfWork接口:
namespace MyApplication.Data.Interfaces { public interface IUnitOfWork { void Save(); } }
DatabaseContext类:
namespace MyApplication.Data.Infra { public class DatabaseContext : DbContext,IUnitOfWork { public DatabaseContext(): base("sqlDatabaseConnectionString") { Database.SetInitializer<DatabaseContext>(null); } protected override void OnModelCreating(DbModelBuilder modelBuilder) { // Same code. base.OnModelCreating(modelBuilder); } #region Entities mapping public DbSet<User> User { get; set; } // >>> A lot of tables #endregion public void Save() { base.SaveChanges(); } } }
IGenericRepository接口:
namespace MyApplication.Data.Interfaces { public interface IGenericRepository<T> where T : class { IQueryable<T> GetAll(); IQueryable<T> Get(Expression<Func<T,bool>> predicate); T Find(params object[] keys); T GetFirstOrDefault(Expression<Func<T,bool>> predicate); bool Any(Expression<Func<T,bool>> predicate); void Insert(T entity); void Edit(T entity); void Delete(Expression<Func<T,bool>> predicate); } }
GenericRepository类:
namespace MyApplication.Data.Repositories { public class GenericRepository<T> : IDisposable,IGenericRepository<T> where T : class { private DatabaseContext _context; private DbSet<T> _entity; public GenericRepository(IUnitOfWork unitOfWork) { if (unitOfWork == null) throw new ArgumentNullException("unitofwork"); _context = unitOfWork as DatabaseContext; _entity = _context.Set<T>(); } public IQueryable<T> GetAll() { return _entity; } public IQueryable<T> Get(Expression<Func<T,bool>> predicate) { return _entity.Where(predicate).AsQueryable(); } // I delete some of the code to reduce the file size. #region Dispose public void Dispose() { // HERE IS MY FIRST DOUBT: MY METHOD ITS OK?! // I saw implementations with GC.Suppress... and dispose in destructor,etc. _context.Dispose(); } #endregion } }
IUserRepository接口:
namespace MyApplication.Data.Interfaces { public interface IUserRepository : IGenericRepository<User> { } }
UserRepository类:
namespace MyApplication.Data.Repositories { public class UserRepository : GenericRepository<User>,IUserRepository,IDisposable { public UserRepository(IUnitOfWork unitOfWork) : base(unitOfWork) {} } }
UserController控制器类:
namespace MyApplication.Presentation.MVCWeb.Controllers { [Authorize] public class UserController : Controller { private IUserRepository _userRepository; private IProfileRepository _profileRepository; private IUnitOfWork _unitOfWork; public UserController() { this._unitOfWork = new DatabaseContext(); this._userRepository = new UserRepository(_unitOfWork); this._profileRepository = new ProfileRepository(_unitOfWork); } public ActionResult List() { return View(this._userRepository.GetAll().ToList()); } public ActionResult Create() { ViewBag.Profiles = new SelectList(this._profileRepository.GetAll().ToList(),"Id","Name"); return View(new User()); } [HttpPost] [ValidateAntiForgeryToken] public ActionResult Create([Bind(Exclude = "Id,Status,CompanyId")] User model) { ViewBag.Profiles = new SelectList(this._profileRepository.GetAll().ToList(),"Name"); if (ModelState.IsValid) { model.EmpresaId = 1; model.Status = Status.Active; _userRepository.Insert(model); _unitOfWork.Save(); return RedirectToAction("List"); } else { return View(); } } }
那么,我何时以及如何处置我的控制器和/或我的存储库和上下文?
解决方法
我的控制器示例:
private IGenericRepository<Profile> _repository; private IUnitOfWork _unitOfWork = new DatabaseContext(); public ProfileController() { this._repository = new GenericRepository<Profile>(_unitOfWork); } public class ProfileController : Controller { private IGenericRepository<Profile> _repository; private IUnitOfWork _unitOfWork = new DatabaseContext(); public ProfileController() { this._repository = new GenericRepository<Profile>(_unitOfWork); } }
使用您现在拥有的代码,最好的办法是覆盖Controller.Dispose(bool disposing)并在那里处理存储库.
protected override void Dispose(bool disposing) { if (disposing) { IDisposable d = _repository as IDisposable; if (d != null) d.Dispose(); GC.SupressFinalize(this); } base.Dispose(disposing); }
一旦开始使用IOC容器,所有这些处理代码都将消失.施工和处置应在集装箱层面进行.容器将是唯一知道或关心存储库和工作单元是一次性的地方.
但我怀疑这些类别中没有一个首先需要是一次性的.您应该在using块中使用sqlConnection.它不需要是DatabaseContext中的类级别字段.
请原谅这个答案的长度.我必须建立一些基本原则才能使我的建议有意义.
固体.
SOLID …代表面向对象编程和设计的五个基本原则.这里关注的两个原则是I和S.
接口隔离原理(ISP)
在IGenericRepository< T>上包括IDisposable.接口明确违反了ISP.
这样做是因为存储库的可处置性(以及正确处理对象的必要性)与其设计目的无关,即获取和存储聚合根对象.通过将接口组合在一起,您将获得一个非隔离的接口.
除了违反一些理论原则之外,为什么这很重要?我将在下面解释.但首先我必须介绍另一个SOLID原则:
单一责任原则
当我将功能代码重新编入优秀的OO代码时,我始终保留这篇文章,Taking the Single Responsibility Principle Seriously.这不是一个容易的主题,文章非常密集.但它作为对SRP的彻底解释是非常宝贵的.
理解SRP并忽略了99.9%的MVC控制器中存在的缺陷,这些控制器采用了许多DI构造函数参数,这里涉及的一个缺陷是:
使控制器负责使用存储库和处理存储库交叉到不同的抽象级别,这违反了SRP.
说明:
因此,您在存储库对象上至少调用了两个公共方法.一个用于获取对象,另一个用于处理存储库.这没什么不对,对吧?通常不会,在存储库或任何对象上调用两个方法没有任何问题.
但Dispose()很特别.处理物体的惯例是在处置后它将不再有用.此约定是使用模式建立单独代码块的一个原因:
using (var foo = new Bar()) { ... // This is the code block } foo.DoSomething(); // <- Outside the block,this does not compile
这在技术上是合法的:
var foo = new Bar(); using (foo) { ... // This is the code block } foo.DoSomething(); // <- Outside the block,this will compile
但是这会在处理完对象后发出警告.这是不正确的,这就是为什么你在MS文档中没有看到这种用法的例子.
由于这种独特的约定,Dispose()与构造和破坏对象的关系比与对象的其他成员的使用密切相关,即使它是作为一个简单的公共方法公开的.
构造和处置处于同样低的抽象层次.但是因为控制器本身并不构建存储库,所以它存在于更高的抽象层次上.在处置存储库时,它会到达其抽象级别之外,以便在不同级别上调整存储库对象.这违反了SRP.
代码现实
好的,就我的代码而言,所有这些理论究竟意味着什么?
考虑控制器代码在处置存储库本身时的样子:
public class CustomerController : Controller { IGenericRepository<Customer> _customerRepo; IMapper<Customer,Customerviewmodel> _mapper; public CustomerController( IMapper<Customer,Customerviewmodel> customerRepository,IMapper<Customer,Customerviewmodel> customerMapper) { _customerRepo = customerRepository; _customerMapper = customerMapper; } public ActionResult Get(int id) { Customerviewmodel vm; using (_customerRepo) // <- This looks fishy { Customer cust = _customerRepo.Get(id); vm = _customerMapper.MapToviewmodel(cust); } return View(wm); } public ActionResult Update(Customerviewmodel vm) { Customer cust = _customerMapper.MapToModel(vm); Customerviewmodel updatedVm; using(_customerRepo) // <- Smells like 3 week old flounder,actually { Customer updatedCustomer = _customerRepo.Store(cust); updatedVm = _customerMapper.MapToviewmodel(updatedCustomer); } return View(updatedVm); } }
控制器在构造时必须接收有用的(非处置的)存储库.这是一种普遍的期望.但是不要在控制器中调用两个方法,否则它会中断.这个控制器只是一次性交易.另外,你甚至无法从另一个公共方法中调用一个公共方法.例如. Update方法可以在将模型存储在存储库中之后调用Get,以便返回更新的Customer View.但这会爆炸.
结论
将存储库作为参数接收意味着其他东西负责创建存储库.其他东西也应该负责妥善处理存储库.
当对象的生命周期和对象的可能的后续使用不受直接控制时,将对象置于与其(其他)公共成员相同的抽象级别的替代方案是定时炸弹.
IDisposable的规则是这样的:在另一个功能接口声明中继承IDisposable永远不可接受,因为IDisposable从不是功能性问题,而只是一个实现细节.