.NET框架4.0的发行推出了许多优秀的增强功能,其中当首推ADO.NET实体框架。该框架已经克服了以前的许多错误,并提供了一组增强的API,其中包括许多新的LINQ to SQL框架方面的改善。在本文中,我们将使用这些API的功能来创建一个通用版本的数据仓库。
一、实体框架概述
实体框架针对数据模型提供了一些更方便的操作方法。默认情况下,设计器可以生成一个描述数据库的模型。
尽管表格间的映射未必都是1:1的映射。每个表格使用一个ObjectSet加以描述,进而ObjectSet对象又提供了相应的方法来创建、更新或反射实体和实体间的关系。实体框架使用一个实体键(这是一个看上去像EntitySet=Customers;CustomerID=4的值)来唯一标识模型内的一个实体及其标识符。使用实体键,我们就有了一个方法来更新对象、从数据库中查询的对象,等等。
二、创建和更新
让我们首先来看一个基类示例仓库的实现。我想分别地讨论CRUD操作,首先来学习创建和更新操作。
清单1:创建/更新操作
1. public abstract class BaseRepository<T> : IRepository<T> 2. where T : EntityObject 3. { 4. public virtual bool CreateNew(T entity) 5. { 6. if (entity == null) 7. throw new ArgumentNullException("entity"); 8. var ctx = CreateContext(); 9. try 10. { 11. ctx.AddObject(this.GetFullEntitySetName(ctx), entity); 12. ctx.SaveChanges(); 13. return true; 14. } 15. catch (Exception ex) { .. } 16. } 17. protected abstract string GetEntitySetName(AdventureWorksObjectContext context); 18. public virtual bool Update(T entity) 19. { 20. if (entity == null) 21. throw new ArgumentNullException("entity"); 22. var ctx = CreateContext(); 23. entity.EntityKey = ctx.CreateEntityKey(this.GetFullEntitySetName(ctx), 24. entity); 25. try 26. { 27. T oldEntity = (T)ctx.GetObjectByKey(entity.EntityKey); 28. if (oldEntity == null) return false; 29. ctx.ApplyCurrentValues(this.GetFullEntitySetName(ctx), entity); 30. ctx.SaveChanges(); 31. return true; 32. } 33. catch (Exception ex) { .. } 34. } } |
上述代码中,我们的BaseRepository类使用ObjectContext类(需要使用CreateContext方法创建每一个请求)和AddObject方法实现添加新的对象,而通过使用ObjectContext类和AttachTo方法实现更新现有的对象。对于创建对象而言,我们需要知道要更新哪种类型的方法。使用我们的助理GetFullEntitySetName方法可以很好地处理这个问题。这个方法能够返回要添加的标识实体的对象(一个如DotNetSamplesObjectContext.Customers的值)的标识。
对于更新一个对象而言,我们遇到了与上下文有关的问题。每个从数据库中查询的对象都使用ObjectStateManager类中的ObjectContext成员进行跟踪。MVC绑定过程实际上已经构建了它自己的对象副本,并通过反射把这些值注入到此对象中。这意味着我们有一个新的对象,而不是附加到ObjectContext上的对象。
这不是一个大问题,我们首先需要查询旧记录。这将为我们的实体生成一个ObjectStateEntry,并且我们可以成功地执行更新(因为它需要知道旧记录是什么)。该实体还需要使用一个EntityKey实体,提供适当的主键信息(记住,EntityKey是确定出已存在的实体的唯一的方式)。
最后,调用ApplyCurrentValues能够把MVC框架所创建的新的实体值应用到旧实体上。在这里,我们仍然需要使用实体集的名称来唯一标识它。