c# – 如何在存储库中实现IDisposable继承?

前端之家收集整理的这篇文章主要介绍了c# – 如何在存储库中实现IDisposable继承?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我正在创建一个通用存储库,不知道实现dispose功能的正确方法是什么:

我没有使用IoC / DI,但我将来会重构我的代码,所以:

我的代码

IUnitOfWork接口:

  1. namespace MyApplication.Data.Interfaces
  2. {
  3. public interface IUnitOfWork
  4. {
  5. void Save();
  6. }
  7. }

DatabaseContext类:

  1. namespace MyApplication.Data.Infra
  2. {
  3. public class DatabaseContext : DbContext,IUnitOfWork
  4. {
  5. public DatabaseContext(): base("sqlDatabaseConnectionString")
  6. {
  7. Database.SetInitializer<DatabaseContext>(null);
  8. }
  9.  
  10. protected override void OnModelCreating(DbModelBuilder modelBuilder)
  11. {
  12. // Same code.
  13. base.OnModelCreating(modelBuilder);
  14. }
  15.  
  16. #region Entities mapping
  17. public DbSet<User> User { get; set; }
  18. // >>> A lot of tables
  19. #endregion
  20.  
  21. public void Save()
  22. {
  23. base.SaveChanges();
  24. }
  25. }
  26. }

IGenericRepository接口:

  1. namespace MyApplication.Data.Interfaces
  2. {
  3. public interface IGenericRepository<T> where T : class
  4. {
  5. IQueryable<T> GetAll();
  6.  
  7. IQueryable<T> Get(Expression<Func<T,bool>> predicate);
  8.  
  9. T Find(params object[] keys);
  10.  
  11. T GetFirstOrDefault(Expression<Func<T,bool>> predicate);
  12.  
  13. bool Any(Expression<Func<T,bool>> predicate);
  14.  
  15. void Insert(T entity);
  16.  
  17. void Edit(T entity);
  18.  
  19. void Delete(Expression<Func<T,bool>> predicate);
  20. }
  21. }

GenericRepository类:

  1. namespace MyApplication.Data.Repositories
  2. {
  3. public class GenericRepository<T> : IDisposable,IGenericRepository<T> where T : class
  4. {
  5. private DatabaseContext _context;
  6. private DbSet<T> _entity;
  7.  
  8. public GenericRepository(IUnitOfWork unitOfWork)
  9. {
  10. if (unitOfWork == null)
  11. throw new ArgumentNullException("unitofwork");
  12.  
  13. _context = unitOfWork as DatabaseContext;
  14. _entity = _context.Set<T>();
  15. }
  16.  
  17. public IQueryable<T> GetAll()
  18. {
  19. return _entity;
  20. }
  21.  
  22. public IQueryable<T> Get(Expression<Func<T,bool>> predicate)
  23. {
  24. return _entity.Where(predicate).AsQueryable();
  25. }
  26.  
  27. // I delete some of the code to reduce the file size.
  28.  
  29. #region Dispose
  30. public void Dispose()
  31. {
  32. // HERE IS MY FIRST DOUBT: MY METHOD ITS OK?!
  33. // I saw implementations with GC.Suppress... and dispose in destructor,etc.
  34.  
  35. _context.Dispose();
  36. }
  37. #endregion
  38. }
  39. }

IUserRepository接口:

  1. namespace MyApplication.Data.Interfaces
  2. {
  3. public interface IUserRepository : IGenericRepository<User> { }
  4. }

UserRepository类:

  1. namespace MyApplication.Data.Repositories
  2. {
  3. public class UserRepository : GenericRepository<User>,IUserRepository,IDisposable
  4. {
  5. public UserRepository(IUnitOfWork unitOfWork) : base(unitOfWork) {}
  6. }
  7. }

UserController控制器类:

  1. namespace MyApplication.Presentation.MVCWeb.Controllers
  2. {
  3. [Authorize]
  4. public class UserController : Controller
  5. {
  6. private IUserRepository _userRepository;
  7. private IProfileRepository _profileRepository;
  8. private IUnitOfWork _unitOfWork;
  9.  
  10. public UserController()
  11. {
  12. this._unitOfWork = new DatabaseContext();
  13. this._userRepository = new UserRepository(_unitOfWork);
  14. this._profileRepository = new ProfileRepository(_unitOfWork);
  15. }
  16.  
  17. public ActionResult List()
  18. {
  19. return View(this._userRepository.GetAll().ToList());
  20. }
  21.  
  22. public ActionResult Create()
  23. {
  24. ViewBag.Profiles = new SelectList(this._profileRepository.GetAll().ToList(),"Id","Name");
  25.  
  26. return View(new User());
  27. }
  28.  
  29. [HttpPost]
  30. [ValidateAntiForgeryToken]
  31. public ActionResult Create([Bind(Exclude = "Id,Status,CompanyId")] User model)
  32. {
  33. ViewBag.Profiles = new SelectList(this._profileRepository.GetAll().ToList(),"Name");
  34.  
  35. if (ModelState.IsValid)
  36. {
  37. model.EmpresaId = 1;
  38. model.Status = Status.Active;
  39.  
  40. _userRepository.Insert(model);
  41. _unitOfWork.Save();
  42.  
  43. return RedirectToAction("List");
  44. }
  45. else
  46. {
  47. return View();
  48. }
  49. }
  50. }

那么,我何时以及如何处置我的控制器和/或我的存储库和上下文?

解决方法

更新的答案:

我的控制器示例:

  1. private IGenericRepository<Profile> _repository;
  2. private IUnitOfWork _unitOfWork = new DatabaseContext();
  3. public ProfileController()
  4. {
  5. this._repository = new GenericRepository<Profile>(_unitOfWork);
  6. }
  7.  
  8. public class ProfileController : Controller
  9. {
  10. private IGenericRepository<Profile> _repository;
  11. private IUnitOfWork _unitOfWork = new DatabaseContext();
  12. public ProfileController()
  13. {
  14. this._repository = new GenericRepository<Profile>(_unitOfWork);
  15. }
  16. }

使用您现在拥有的代码,最好的办法是覆盖Controller.Dispose(bool disposing)并在那里处理存储库.

  1. protected override void Dispose(bool disposing)
  2. {
  3. if (disposing)
  4. {
  5. IDisposable d = _repository as IDisposable;
  6. if (d != null)
  7. d.Dispose();
  8. GC.SupressFinalize(this);
  9. }
  10. base.Dispose(disposing);
  11. }

一旦开始使用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()很特别.处理物体的惯例是在处置后它将不再有用.此约定是使用模式建立单独代码块的一个原因:

  1. using (var foo = new Bar())
  2. {
  3. ... // This is the code block
  4. }
  5.  
  6. foo.DoSomething(); // <- Outside the block,this does not compile

这在技术上是合法的:

  1. var foo = new Bar();
  2. using (foo)
  3. {
  4. ... // This is the code block
  5. }
  6.  
  7. foo.DoSomething(); // <- Outside the block,this will compile

但是这会在处理完对象后发出警告.这是不正确的,这就是为什么你在MS文档中没有看到这种用法的例子.

由于这种独特的约定,Dispose()与构造和破坏对象的关系比与对象的其他成员的使用密切相关,即使它是作为一个简单的公共方法公开的.

构造和处置处于同样低的抽象层次.但是因为控制器本身并不构建存储库,所以它存在于更高的抽象层次上.在处置存储库时,它会到达其抽象级别之外,以便在不同级别上调整存储库对象.这违反了SRP.

代码现实

好的,就我的代码而言,所有这些理论究竟意味着什么?

考虑控制器代码在处置存储库本身时的样子:

  1. public class CustomerController : Controller
  2. {
  3. IGenericRepository<Customer> _customerRepo;
  4. IMapper<Customer,Customerviewmodel> _mapper;
  5.  
  6. public CustomerController(
  7. IMapper<Customer,Customerviewmodel> customerRepository,IMapper<Customer,Customerviewmodel> customerMapper)
  8. {
  9. _customerRepo = customerRepository;
  10. _customerMapper = customerMapper;
  11. }
  12.  
  13. public ActionResult Get(int id)
  14. {
  15. Customerviewmodel vm;
  16. using (_customerRepo) // <- This looks fishy
  17. {
  18. Customer cust = _customerRepo.Get(id);
  19. vm = _customerMapper.MapToviewmodel(cust);
  20. }
  21. return View(wm);
  22. }
  23.  
  24. public ActionResult Update(Customerviewmodel vm)
  25. {
  26. Customer cust = _customerMapper.MapToModel(vm);
  27. Customerviewmodel updatedVm;
  28. using(_customerRepo) // <- Smells like 3 week old flounder,actually
  29. {
  30. Customer updatedCustomer = _customerRepo.Store(cust);
  31. updatedVm = _customerMapper.MapToviewmodel(updatedCustomer);
  32. }
  33. return View(updatedVm);
  34. }
  35. }

控制器在构造时必须接收有用的(非处置的)存储库.这是一种普遍的期望.但是不要在控制器中调用两个方法,否则它会中断.这个控制器只是一次性交易.另外,你甚至无法从另一个公共方法调用一个公共方法.例如. Update方法可以在将模型存储在存储库中之后调用Get,以便返回更新的Customer View.但这会爆炸.

结论

将存储库作为参数接收意味着其他东西负责创建存储库.其他东西也应该负责妥善处理存储库.

当对象的生命周期和对象的可能的后续使用不受直接控制时,将对象置于与其(其他)公共成员相同的抽象级别的替代方案是定时炸弹.

IDisposable的规则是这样的:在另一个功能接口声明中继承IDisposable永远不可接受,因为IDisposable从不是功能性问题,而只是一个实现细节.

猜你在找的C#相关文章