1_MVC+EF+Autofac(dbfirst)轻型项目框架_core层(以登陆为例)
前言
在上一篇0_MVC+EF+Autofac(dbfirst)轻型项目框架_基本框架中,我已经介绍了这个轻型框架的层次结构,在下面的这篇文章中,我将以教师登陆功能为例,具体来扩充下我的core层的代码。
在这之前,我想先补充讨论下是否有必要添加server层,因为看过不少别人的框架都有这一层。首先,server层在不同地方有着不同的解释。有个常听的词叫MVSC,这里所指的S虽然也是server的意思,但实现的功能更有点类似于我框架中的core,主要存放也是业务逻辑。但我看了别人框架上的server层,有些甚至已经直接继承自Controller类,一开始很不解,这样做让web层中的Controller颜面何存。但在深入了解了mvc的一些机制后觉得这样做也有道理(mvc在注册控制器时是遍历文件夹中的dll来的),把视图和控制器存放在了不同的程序集里,便于团队开发。但有些server层的作用更像是单纯为了给web(view)和core解耦或者说是改变依赖关系。具体我还真的没能力来讨论这么做有没有必要,别人这么做必然有别人的出发点和目的,但在我的轻量架构中还是没有引入server这个层,因为在一定程度上它只会增加框架整体的复杂性,“轻”还是主基调。
同样,文中有问题的地方欢迎批评指正!谢谢!
创建过程
1.首先数据表结构如下图
表中ID是教师的GUID,TID才是用来登陆的唯一标示,PID是权限的标示,接下去讲权限判断时将会提到它。
2.根据数据库生成edmx在Model层的Entity中
3.在ICore中创建教师类接口,在Core中创建教师类
接口中很简单,就一句话,定义了教师类的接口。
public partial interface ITeacher{}
在Core中创建一个类用来实现ITeacher接口
public partial class Teacher : BaseCore<Entity.Teacher>, ICore.ITeacher { public Teacher(DbContext dbContext) { db = dbContext; } }
BaseCore是什么?不要惊慌。在上一篇中,我谈到了我将不引入DAL层来再次封装EF,但在EF中,有些操作比较复杂,例如修改删除等。如果每次使用时都要先查找再修改,要写很多重复代码,我的BaseCore是对这些方法的一些封装,虽然会造成一些代码的污染,但着实可以少写不少重复代码。也许会有人提出异议,这样的行为是否可以理解成是对EF的封装?我也不能完全否认,但我更愿意把它理解成业务层对于操作EF的帮助类:
第一,相对于单独做一层来看,这样更“轻”
第二,这样做放在业务上也更加合适,例如不需要去考虑“分页该不该放在DAL?”这类永远讨论不出正确答案的问题
第三,维护更简单
第四,既然是帮助类,那就提供了不用它的可能,觉得不爽不用它就是
以下是BaseCore的代码,有点长。帮助类中借鉴了部分他人分享的代码,实在找不到出处了,这里表示感谢!
1 using System; 2 using System.Collections.Generic; 3 using System.Data.Entity; 4 using System.Data.Entity.Infrastructure; 5 using System.Linq; 6 using System.Linq.Expressions; 7 using System.Reflection; 8 using System.Text; 9 10 namespace EDUA_Core 11 { 12 /// <summary> 13 /// 业务帮助父类 14 /// </summary> 15 /// <typeparam name="T">之类类型</typeparam> 16 public class BaseCore<T> where T : class,new() 17 { 18 /// <summary> 19 /// EF上下文对象 20 /// </summary> 21 protected DbContext db { get; set; } 22 23 #region 1.0新增实体 + void Add(T model) 24 /// <summary> 25 /// 新增实体 26 /// </summary> 27 /// <param name="model">新增的实体</param> 28 protected void Add(T model) 29 { 30 db.Set<T>().Add(model); 31 } 32 #endregion 33 34 #region 1.0.1新增实体并保存 + int AddAndSave(T model) 35 /// <summary> 36 /// 新增实体并保存 37 /// </summary> 38 /// <param name="model">新增的实体</param> 39 /// <returns>受影响的行数</returns> 40 protected int AddAndSave(T model) 41 { 42 Add(model); 43 return db.SaveChanges(); 44 } 45 #endregion 46 47 #region 2.0删除实体 + void Del(T model) 48 /// <summary> 49 /// 删除实体 50 /// </summary> 51 /// <param name="model">删除的实体</param> 52 protected void Del(T model) 53 { 54 db.Set<T>().Attach(model); 55 db.Set<T>().Remove(model); 56 } 57 #endregion 58 59 #region 2.0.1删除实体并保存 + int DelAndSave(T model) 60 /// <summary> 61 /// 删除实体并保存 62 /// </summary> 63 /// <param name="model">删除的实体</param> 64 /// <returns>受影响的行数</returns> 65 protected int DelAndSave(T model) 66 { 67 Del(model); 68 return db.SaveChanges(); 69 } 70 #endregion 71 72 #region 2.1根据条件删除 + void DelBy(Expression<Func<T, bool>> delWhere) 73 /// <summary> 74 /// 根据条件删除 75 /// </summary> 76 /// <param name="delWhere">条件</param> 77 protected void DelBy(Expression<Func<T, bool>> delWhere) 78 { 79 //3.1查询要删除的数据 80 List<T> listDeleting = db.Set<T>().Where(delWhere).ToList(); 81 //3.2将要删除的数据 用删除方法添加到 EF 容器中 82 listDeleting.ForEach(u => 83 { 84 db.Set<T>().Attach(u);//先附加到 EF容器 85 db.Set<T>().Remove(u);//标识为 删除 状态 86 }); 87 } 88 #endregion 89 90 #region 2.1.1根据条件删除并保存 + int DelByAndSave(Expression<Func<T, bool>> delWhere) 91 /// <summary> 92 /// 根据条件删除并保存 93 /// </summary> 94 /// <param name="delWhere">条件</param> 95 /// <returns>受影响的行数</returns> 96 protected int DelByAndSave(Expression<Func<T, bool>> delWhere) 97 { 98 DelBy(delWhere); 99 return db.SaveChanges(); 100 } 101 #endregion 102 103 #region 3.0修改实体 + void Modify(T model, params string[] proNames) 104 /// <summary> 105 /// 修改,如: 106 /// T u = new T() { uId = 1, uLoginName = "asdfasdf" }; 107 /// this.Modify(u, "uLoginName"); 108 /// </summary> 109 /// <param name="model">要修改的实体对象</param> 110 /// <param name="proNames">要修改的 属性 名称</param> 111 protected void Modify(T model, params string[] proNames) 112 { 113 //4.1将 对象 添加到 EF中 114 DbEntityEntry entry = db.Entry<T>(model); 115 //4.2先设置 对象的包装 状态为 Unchanged 116 entry.State = (System.Data.Entity.EntityState)System.Data.EntityState.Unchanged; 117 //4.3循环 被修改的属性名 数组 118 foreach (string proName in proNames) 119 { 120 //4.4将每个 被修改的属性的状态 设置为已修改状态;后面生成update语句时,就只为已修改的属性 更新 121 entry.Property(proName).IsModified = true; 122 } 123 } 124 #endregion 125 126 #region 3.0.1修改和保存 + int ModifyAndSave(T model, params string[] proNames) 127 /// <summary> 128 /// 修改和保存,如: 129 /// T u = new T() { uId = 1, uLoginName = "asdfasdf" }; 130 /// this.Modify(u, "uLoginName"); 131 /// </summary> 132 /// <param name="model">要修改的实体对象</param> 133 /// <param name="proNames">要修改的 属性 名称</param> 134 /// <returns>受影响的行数</returns> 135 protected int ModifyAndSave(T model, params string[] proNames) 136 { 137 Modify(model, proNames); 138 return db.SaveChanges(); 139 } 140 #endregion 141 142 #region 3.1批量修改 + void ModifyBy(T model, Expression<Func<T, bool>> whereLambda, params string[] modifiedProNames) 143 /// <summary> 144 /// 批量修改 145 /// </summary> 146 /// <param name="model">要修改的实体对象</param> 147 /// <param name="whereLambda">查询条件</param> 148 /// <param name="proNames">要修改的 属性 名称</param> 149 protected void ModifyBy(T model, Expression<Func<T, bool>> whereLambda, params string[] modifiedProNames) 150 { 151 //4.1查询要修改的数据 152 List<T> listModifing = db.Set<T>().Where(whereLambda).ToList(); 153 154 //获取 实体类 类型对象 155 Type t = typeof(T); // model.GetType(); 156 //获取 实体类 所有的 公有属性 157 List<PropertyInfo> proInfos = t.GetProperties(BindingFlags.Instance | BindingFlags.Public).ToList(); 158 //创建 实体属性 字典集合 159 Dictionary<string, PropertyInfo> dictPros = new Dictionary<string, PropertyInfo>(); 160 //将 实体属性 中要修改的属性名 添加到 字典集合中 键:属性名 值:属性对象 161 proInfos.ForEach(p => 162 { 163 if (modifiedProNames.Contains(p.Name)) 164 { 165 dictPros.Add(p.Name, p); 166 } 167 }); 168 169 //4.3循环 要修改的属性名 170 foreach (string proName in modifiedProNames) 171 { 172 //判断 要修改的属性名是否在 实体类的属性集合中存在 173 if (dictPros.ContainsKey(proName)) 174 { 175 //如果存在,则取出要修改的 属性对象 176 PropertyInfo proInfo = dictPros[proName]; 177 //取出 要修改的值 178 object newValue = proInfo.GetValue(model, null); //object newValue = model.uName; 179 180 //4.4批量设置 要修改 对象的 属性 181 foreach (T usrO in listModifing) 182 { 183 //为 要修改的对象 的 要修改的属性 设置新的值 184 proInfo.SetValue(usrO, newValue, null); //usrO.uName = newValue; 185 } 186 } 187 } 188 } 189 #endregion 190 191 #region 3.1.1批量修改并保存 + int ModifyByAndSave(T model, Expression<Func<T, bool>> whereLambda, params string[] modifiedProNames) 192 /// <summary> 193 /// 批量修改并保存 194 /// </summary> 195 /// <param name="model">要修改的实体对象</param> 196 /// <param name="whereLambda">查询条件</param> 197 /// <param name="proNames">要修改的 属性 名称</param> 198 /// <returns>受影响的行数</returns> 199 protected int ModifyByAndSave(T model, Expression<Func<T, bool>> whereLambda, params string[] modifiedProNames) 200 { 201 ModifyBy(model, whereLambda, modifiedProNames); 202 return db.SaveChanges(); 203 } 204 #endregion 205 206 #region 4.0根据条件获取记录条数 + int GetCountBy(Expression<Func<T, bool>> whereLambda) 207 /// <summary> 208 /// 根据条件获取记录条数 209 /// </summary> 210 /// <returns>总记录条数</returns> 211 protected int GetCountBy(Expression<Func<T, bool>> whereLambda) 212 { 213 return db.Set<T>().Where(whereLambda).Count(); 214 } 215 #endregion 216 217 #region 5.0获取所有记录 + List<T> GetAllList() 218 /// <summary> 219 /// 获取所有记录 220 /// </summary> 221 /// <returns>返回的集合</returns> 222 protected List<T> GetAllList() 223 { 224 return db.Set<T>().ToList(); 225 } 226 #endregion 227 228 #region 5.1根据条件查询 + List<T> GetListBy(Expression<Func<T, bool>> whereLambda) 229 /// <summary> 230 /// 根据条件查询 231 /// </summary> 232 /// <param name="whereLambda">条件Lambda表达式</param> 233 /// <returns>返回的集合</returns> 234 protected List<T> GetListBy(Expression<Func<T, bool>> whereLambda) 235 { 236 return db.Set<T>().Where(whereLambda).ToList(); 237 } 238 #endregion 239 240 #region 5.2根据条件 排序 和查询 + List<T> GetListBy<TKey>(Expression<Func<T, bool>> whereLambda, Expression<Func<T, TKey>> orderLambda, bool IsDes = false) 241 /// <summary> 242 /// 根据条件 排序 和查询 243 /// </summary> 244 /// <typeparam name="TKey">排序字段类型</typeparam> 245 /// <param name="whereLambda">查询条件 lambda表达式</param> 246 /// <param name="orderLambda">排序条件 lambda表达式</param> 247 /// // <param name="IsDes">是否逆序</param> 248 /// <returns>返回的集合</returns> 249 protected List<T> GetListBy<TKey>(Expression<Func<T, bool>> whereLambda, Expression<Func<T, TKey>> orderLambda, bool IsDes = false) 250 { 251 if(IsDes) 252 return db.Set<T>().Where(whereLambda).OrderByDescending(orderLambda).ToList(); 253 return db.Set<T>().Where(whereLambda).OrderBy(orderLambda).ToList(); 254 } 255 #endregion 256 257 #region 5.3分页查询 + List<T> GetPagedList<TKey>(int pageIndex, int pageSize, Expression<Func<T, bool>> whereLambda, Expression<Func<T, TKey>> orderBy, bool IsDes = false) 258 /// <summary> 259 /// 分页查询 260 /// </summary> 261 /// <param name="pageIndex">页码</param> 262 /// <param name="pageSize">页容量</param> 263 /// <param name="whereLambda">条件 lambda表达式</param> 264 /// <param name="orderBy">排序 lambda表达式</param> 265 /// <param name="IsDes">是否逆序</param> 266 /// <returns>返回的集合</returns> 267 protected List<T> GetPagedList<TKey>(int pageIndex, int pageSize, Expression<Func<T, bool>> whereLambda, Expression<Func<T, TKey>> orderBy, bool IsDes = false) 268 { 269 if (IsDes) 270 return db.Set<T>().Where(whereLambda).OrderByDescending(orderBy).Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList(); 271 return db.Set<T>().Where(whereLambda).OrderBy(orderBy).Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList(); 272 } 273 #endregion 274 275 } 276 }
可以看出,在上面的代码中,我提供了增删改查的大部分方法,对于写入性的操作,我提供了是否立刻SaveChange的选择,如果立刻save,可以调用类似于AddAndSave()这样的方法,如果想多次调用后统一保存以提高性能,可以调用Add()这类方法,在所有操作结束后,再调用saveChange的方法。这个帮助类中存在一个DbContext类型的属性,它用来存放ef上下文,在teacher的实现类中,提供了它的构造方法,将dbcontext传入。帮助类中几乎所有的操作都依赖于这个DbContext。
上面teacher的接口和实现都是有partial修饰,是一个部分类(接口),这样做的目的是为了便于管理,例如日后需要添加一个学生类时可以这样
1 public partial class Student : BaseCore<Entity.Student>, ICore.IStudent 2 { 3 public Student(DbContext dbContext) 4 { 5 db = dbContext; 6 } 7 } 8 9 public partial class Teacher : BaseCore<Entity.Teacher>, ICore.ITeacher 10 { 11 public Teacher(DbContext dbContext) 12 { 13 db = dbContext; 14 } 15 }
这些代码是重复的,可将它们写在同一个cs中,如果有需要可以用T4模板自动生成,但事实上并不是每张表都需要生成对应的实体,所以我选择手动来添加它们。教师和学生具体的登陆的逻辑可能不同,将它们放在另一个partial类中会更加合理。
4.创建教师的部分接口和部分类
接口如下
1 public partial interface ITeacher 2 { 3 #region 教师登录 + ReturnVal Login(string tid, string pwd); 4 /// <summary> 5 /// 教师登录 6 /// </summary> 7 /// <param name="tid">教师id</param> 8 /// <param name="pwd">密码</param> 9 /// <returns>约定返回类型</returns> 10 ReturnVal Login(string tid, string pwd); 11 #endregion 12 }
实现如下
1 public partial class Teacher 2 { 3 4 #region 教师登录操作 + WebModel.ReturnVal Login(string tid, string pwd) 5 /// <summary> 6 /// 教师登录操作 7 /// </summary> 8 /// <param name="tid">教师登录名</param> 9 /// <param name="pwd">密码</param> 10 /// <returns>约定返回类</returns> 11 public WebModel.ReturnVal Login(string tid, string pwd) 12 { 13 int iTID = Convert.ToInt32(tid); 14 Entity.Teacher teacher = this.GetListBy(t => (t.TID == iTID) && (t.IsDel == false)).ToList().FirstOrDefault(); 15 if (teacher == null) 16 { 17 return new ReturnVal(ReturnStatu.Failure, "用户不存在"); 18 } 19 else 20 { 21 if (EDUA_Util.EncrypHelper.MD5(pwd) == teacher.Password) 22 { 23 return new ReturnVal(ReturnStatu.Success, "登陆成功", WebModel.Teacher.ToWebModel(teacher)); 24 } 25 else 26 { 27 return new ReturnVal(ReturnStatu.Failure, "密码错误"); 28 } 29 } 30 } 31 #endregion 32 }
很简单,首先判断用户是否存在,接下去判断密码是否正确。
第13行,这里我使用了我的basecore里的方法,如果不想用也很简单。
返回值类型为ReturnVal,这是我自己的类,原因是不能简单地用一个bool值来传递登陆成功与否,如果登陆成功了需要将对象返回保存在cookie,session中,以保证状态的维持。ReturnVal也会被用在其他core与web传递数据的过程中。ReturnVal类如下
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace WebModel 8 { 9 /// <summary> 10 /// 返回值约定 11 /// 从core 到 web 返回内容的封装 12 /// </summary> 13 public class ReturnVal 14 { 15 #region 属性 16 /// <summary> 17 /// 返回的状态 18 /// </summary> 19 public ReturnStatu Statu { get; set; } 20 /// <summary> 21 /// 返回的消息 22 /// </summary> 23 public string Message { get; set; } 24 /// <summary> 25 /// 返回的数据 26 /// </summary> 27 public object Data { get; set; } 28 #endregion 29 30 #region 构造方法 31 /// <summary> 32 /// 构造方法 33 /// </summary> 34 /// <param name="statu">状态</param> 35 public ReturnVal(ReturnStatu statu) 36 { 37 this.Statu = statu; 38 } 39 /// <summary> 40 /// 构造方法 41 /// </summary> 42 /// <param name="statu">状态</param> 43 /// <param name="message">消息</param> 44 public ReturnVal(ReturnStatu statu, string message) 45 : this(statu) 46 { 47 this.Message = message; 48 } 49 /// <summary> 50 /// 构造方法 51 /// </summary> 52 /// <param name="statu">状态</param> 53 /// <param name="message">消息</param> 54 /// <param name="data">数据</param> 55 public ReturnVal(ReturnStatu statu, string message, object data) 56 : this(statu, message) 57 { 58 this.Data = data; 59 } 60 #endregion 61 } 62 63 64 /// <summary> 65 /// 状态枚举 66 /// </summary> 67 public enum ReturnStatu 68 { 69 /// <summary> 70 /// 成功 71 /// </summary> 72 Success, 73 /// <summary> 74 /// 失败 75 /// </summary> 76 Failure, 77 /// <summary> 78 /// 错误 79 /// </summary> 80 Err 81 } 82 }