DB First 中对Repository 层封装的几点小记
在数据库表创建完成的情况下,使用DB First 进行开发,封装底层会遇到一些小问题,在此记录一下,供以后参考。
主要解决的问题有:
1、EF上下文管理
2、BaseRepository的封装
3、EF中实体序列化的问题
1、EF上下文管理
EF操作数据库的上下文,要达到在用户每一次的请求操作中都只有一个上下文,当用户通过Http请求结束后要释放用户使用的上下文资源,也就
是EF生成中的ObjectContext不能每次使用时候通过new 创建一个新的实例,一次请求只需要创建一次,请求结束ObjectContext资源释放。
这里要用到的就是单例模式和简单工厂了,单例保证只有一个ObjectContext ,每次调用通过工厂方式获取。这里直接贴代码
1 public class DbContextFactory 2 { 3 private static string efKey = "ef_key"; 4 5 public static RBACEntities GetContext() 6 { 7 RBACEntities db = CallContext.GetData(efKey) as RBACEntities; 8 if (db == null) 9 { 10 db = new RBACEntities(); 11 CallContext.SetData(efKey,db); 12 } 13 Debug.WriteLine("EF上下文获取:" + db.GetHashCode() + ",时间:" + DateTime.Now.ToString("hh-mm-ss ffff")); 14 return db; 15 } 16 17 public static void Dispose() 18 { 19 var context=GetContext(); 20 Debug.WriteLine("EF上下文释放:" + context.GetHashCode() + ",时间:" + DateTime.Now.ToString("hh-mm-ss ffff")); 21 context.Dispose(); 22 } 23 }
然后,在Global.asax中,在请求结束事件中调用释放资源方法。
1 protected void Application_EndRequest() 2 { 3 Dedeyi.RBAC2.Core.Model.DbContextFactory.Dispose(); 4 }
Debug.WriteLine是调试时候,追踪一下上下文的获取和释放是否达到目的。打开调试的Output ,检测到一次请求的输出信息
可以看到,在这次请求处理中使用了两次上下文实例,都是同一个实例(HashCode相同),最后又一次的资源释放。
2、BaseRepository的封装
基仓储的封装。要实现基本的增删查改操作。在这里因为使用的是Entity Framework 中的ObjectContext进行数据库交互,其灵活性是很强的
如果只是像ADO.NET中通过sql对CRUD的封装那样,就很多地方没法发挥ObjectContex的作用了。
所有我们在基类中要把这个上下文留出来,作为一个可以访问的属性。通过这个上下文,可以在自类中访问数据模型中其他表,ObjectSet 是对应当前EF实体对象,也把这个暴露给外面使用。然后就是分页和搜索的封装了。具体代码如下:
1 public class BaseRepository<TEntity> where TEntity: EntityObject 2 { 3 protected RBACEntities RBACContext { get; set; } 4 protected ObjectSet<TEntity> DbSet { get; set; } 5 6 protected virtual IQueryable<TEntity> Entities 7 { 8 get { return RBACContext.CreateObjectSet<TEntity>(); } 9 } 10 11 public BaseRepository() 12 { 13 this.RBACContext = DbContextFactory.GetContext(); 14 this.DbSet=RBACContext.CreateObjectSet<TEntity>(); 15 } 16 17 public IQueryable<TEntity> GetPage<TKey>(int pageIndex, int pageSize,Expression<Func<TEntity,TKey>> orderBy, out int totalRecord) 18 { 19 totalRecord = DbSet.Count(); 20 return this.DbSet 21 .OrderByDescending(orderBy) 22 .Skip((pageIndex - 1) * pageSize) 23 .Take(pageSize) 24 .AsQueryable(); 25 } 26 27 public IQueryable<TEntity> GetPage<TKey>(int pageIndex, int pageSize, Expression<Func<TEntity, TKey>> orderBy, Expression<Func<TEntity, bool>> where, out int totalRecord) 28 { 29 var res=DbSet.Where(where); 30 31 totalRecord = res.Count(); 32 return res 33 .OrderByDescending(orderBy) 34 .Skip((pageIndex - 1) * pageSize) 35 .Take(pageSize) 36 .AsQueryable(); 37 } 38 39 40 public TEntity Add(TEntity entity,bool saveChange) 41 { 42 RBACContext.CreateObjectSet<TEntity>().AddObject(entity); 43 if (saveChange) 44 { 45 RBACContext.SaveChanges(); 46 } 47 return entity; 48 } 49 50 public TEntity Edit(TEntity entity, bool saveChange) 51 { 52 RBACContext.CreateObjectSet<TEntity>().Attach(entity); 53 RBACContext.ObjectStateManager.ChangeObjectState(entity,System.Data.EntityState.Modified); 54 if (saveChange) 55 { 56 RBACContext.SaveChanges(); 57 } 58 return entity; 59 } 60 61 public void Delete(TEntity entity, bool saveChange) 62 { 63 RBACContext.CreateObjectSet<TEntity>().DeleteObject(entity); 64 if (saveChange) { RBACContext.SaveChanges(); } 65 } 66 }
SaveChange是一个bool 类型参数,表明是否要上下文保存到数据库,如果只是修改一张表,直接保存,如果涉及多个操作,最后统一SaveChange也就实现了事务统一。
3、EF中实体序列化去除 EntityKey字段
如果直接把EF模型数据表对应的模型进行序列化,会得到很多跟EF相关的属性,要去掉这些多余的属性,就响应对序列化进行一些修改了。
这里使用的是Newtonsoft.Net进行序列化的。具体封装源码:
1 public class JsonNet 2 { 3 /// <summary> 4 /// 将实体对象转换成Json字符串 5 /// </summary> 6 /// <param name="item"></param> 7 /// <returns></returns> 8 public static string SerializeToString(object item) 9 { 10 return JsonConvert.SerializeObject(item, Formatting.Indented, 11 new JsonSerializerSettings 12 { 13 ReferenceLoopHandling = ReferenceLoopHandling.Ignore, 14 ContractResolver = new ExcludePropertiesContractResolver(new List<string> { "EntityKey" }), 15 }); 16 17 } 18 19 /// <summary> 20 /// 将Json字符串转换成实体对象 21 /// </summary> 22 /// <typeparam name="T"></typeparam> 23 /// <param name="jsonString"></param> 24 /// <returns></returns> 25 public static T DeserializeToEntity<T>(string jsonString) 26 { 27 return JsonConvert.DeserializeObject<T>(jsonString); 28 } 29 } 30 31 public class ExcludePropertiesContractResolver : DefaultContractResolver 32 { 33 IEnumerable<string> lstExclude; 34 35 public ExcludePropertiesContractResolver(IEnumerable<string> excludedProperties) 36 { 37 lstExclude = excludedProperties; 38 } 39 40 protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) 41 { 42 return base.CreateProperties(type, memberSerialization).ToList().FindAll(p => !lstExclude.Contains(p.PropertyName)); 43 } 44 }