前沿

  园子里已有挺多博文介绍了EFCore+Mysql/MSSql如何进行使用,但实际开发不会把EF层放在Web层混合起来,需要多个项目配合结构清晰的进行分层工作,本文根据个人实践经验总结将各个项目进行分层,仅供想自己搭建,包含数据仓储以及分页多字段排序。

目录结构

1.实体层(EF)搭建

1.1添加Nuget包

1.2添加实体

1.3构造DbContext

1.4数据迁移生成数据库

2.仓储层搭建

  2.1添加Nuget包

  2.2添加必要的支持IEnumerable<T>和IQueryable<T> 的OrderBy字符串支持类LinqExtensions

  2.3构造RepositoryBase<T>

  2.4添加Table文件夹,添加SysUserRepository

  2.5添加工作单元UnitOfWork

3.WebApi项目测试

  3.1注入EF

  3.2测试

4.Github项目地址

正文

1.实体层(EF)搭建

新建.NetCore类库项目Entity,本人使用的是2.0的SDK

1.1添加Nuget包

  1. PM> Install-Package Microsoft.AspNetCore.All -version 2.0.9
  1. PM> Install-Package Pomelo.EntityFrameworkCore.MySql -version 2.0.1

1.2添加实体

父类EntityBase

  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel.DataAnnotations;
  4. using System.Text;
  5.  
  6. namespace Entity.Table
  7. {
  8. public class EntityBase
  9. {
  10. /// <summary>
  11. /// 创建时间
  12. /// </summary>
  13. [Display(Name = "创建时间")]
  14. public DateTime? CreateTime { get; set; } = DateTime.Now;
  15.  
  16. /// <summary>
  17. /// 创建人
  18. /// </summary>
  19. [Display(Name = "创建人")]
  20. [StringLength(32)]
  21. public string CreateUser { get; set; }
  22.  
  23. /// <summary>
  24. /// 状态0-删除,1-正常,2-禁用,3-待审核
  25. /// </summary>
  26. [Display(Name = "状态0-逻辑删除,1-正常,2-禁用,...")]
  27. public virtual int? Status { get; set; } = 2;
  28.  
  29. /// <summary>
  30. /// 排序
  31. /// </summary>
  32. [Display(Name = "排序")]
  33. public int? Sort { get; set; } = 0;
  34.  
  35. /// <summary>
  36. /// 备注
  37. /// </summary>
  38. [Display(Name = "备注")]
  39. [StringLength(200)]
  40. public string Remark { get; set; } = "";
  41. }
  42. }

实体类SysUser

  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel.DataAnnotations;
  4. using System.ComponentModel.DataAnnotations.Schema;
  5. using System.Text;
  6.  
  7. namespace Entity.Table
  8. {
  9. [Table("Sys_User")]
  10. public class SysUser : EntityBase
  11. {
  12. [Key]
  13. [StringLength(32)]
  14. public string SysUserId { get; set; }
  15.  
  16. [Display(Name = "用户名")]
  17. [Required]
  18. [StringLength(32)]
  19. public string UserName { get; set; }
  20.  
  21. [Display(Name = "密码")]
  22. [StringLength(255)]
  23. public string Password { get; set; }
  24.  
  25. [Display(Name = "真实姓名")]
  26. [StringLength(32)]
  27. public string TrueName { get; set; }
  28.  
  29. [Display(Name = "昵称")]
  30. [StringLength(32)]
  31. public string NikeName { get; set; }
  32.  
  33. [Display(Name = "手机号")]
  34. [StringLength(20)]
  35. public string Mobile { get; set; }
  36.  
  37. [Display(Name = "邮箱")]
  38. [EmailAddress]
  39. [StringLength(100)]
  40. public string Email { get; set; }
  41.  
  42. [Display(Name = "QQOpenid")]
  43. [StringLength(200)]
  44. public string QQ { get; set; }
  45.  
  46. [Display(Name = "微信openid")]
  47. [StringLength(200)]
  48. public string WX { get; set; }
  49.  
  50. [Display(Name = "头像")]
  51. [StringLength(255)]
  52. public string Avatar { get; set; }
  53.  
  54. [Display(Name = "性别")]
  55. [StringLength(1)]
  56. public string Sex { get; set; }
  57.  
  58. [Display(Name = "用户类型")]
  59. [StringLength(1)]
  60. public string UserType { get; set; }//0-前台用户,1-管理用户
  61.  
  62. }
  63. }

1.3构造DbContext

  1. using Entity.Table;
  2. using Microsoft.EntityFrameworkCore;
  3. using Microsoft.Extensions.Configuration;
  4. using System;
  5.  
  6. namespace Entity
  7. {
  8. public class AeDbContext : DbContext
  9. {
  10. #region 构造方法
  11. public AeDbContext(DbContextOptions<AeDbContext> options) : base(options) { }
  12. public AeDbContext() { } //非注入构造方式
  13. #endregion
  14.  
  15. #region 表对象
  16. public virtual DbSet<SysUser> SysUsers { get; set; }
  17. #endregion
  18.  
  19. protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
  20. {
  21. base.OnConfiguring(optionsBuilder);
  22. if (!optionsBuilder.IsConfigured)
  23. {
  24. //重点:数据迁移或者直接New AeDbContext时候用到的链接字符串获取方式
  25. var builder = new ConfigurationBuilder()
  26. .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
  27. var configuration = builder.Build();
  28. string connectionString = configuration.GetConnectionString("SQLConnection");
  29. optionsBuilder.UseMySql(connectionString);
  30. }
  31. }
  32. }
  33. }

在这有重写OnConfiguring方法,如果没有构造数据库链接字符串的话则到appsettings.json中去取,注意将appsettings.json文件始终复制

appsettings.json

  1. {
  2. "ConnectionStrings": {
  3. "SQLConnection": "server=127.0.0.1;database=eftest;userid=root;pwd=123456;port=3306;sslmode=none;"
  4. },
  5. "server.urls": "http://localhost:5001" //监听端口配置,可多个
  6. }

1.4数据迁移生成数据库

打开PM选择默认项目Entity

输入

  1. PM> Add-Migration init

若提示The configuration file 'appsettings.json' was not found and is not optional. The physical path is 'E:\VS项目\EFTest\Entity\bin\Debug\netcoreapp2.0\appsettings.json'.

则为没检测到appsettings.json,需要文件更改属性为复制

提示后进行更新数据库,如下为更新成功。

  1. PM> update-database

最后Entity项目内容如下

2.仓储层搭建

新建.NetCore类库项目Repository并引用项目Entity

2.1添加Nuget包

  1. PM> Install-Package Microsoft.EntityFrameworkCore -version 2.0.3
  1. PM> Install-Package LinqKit.Microsoft.EntityFrameworkCore -version 1.1.15

2.2添加必要的支持IEnumerable<T>和IQueryable<T> 的OrderBy字符串支持类LinqExtensions

  1. using System;
  2. using System.Linq;
  3. using System.Linq.Expressions;
  4. using System.Reflection;
  5. using System.Collections.Generic;
  6.  
  7. namespace Repository
  8. {
  9. internal static class LinqExtensions {
  10. private static PropertyInfo GetPropertyInfo(Type objType, string name) {
  11. var properties = objType.GetProperties();
  12. var matchedProperty = properties.FirstOrDefault(p => p.Name == name);
  13. if (matchedProperty == null) {
  14. throw new ArgumentException("name");
  15. }
  16.  
  17. return matchedProperty;
  18. }
  19. private static LambdaExpression GetOrderExpression(Type objType, PropertyInfo pi) {
  20. var paramExpr = Expression.Parameter(objType);
  21. var propAccess = Expression.PropertyOrField(paramExpr, pi.Name);
  22. var expr = Expression.Lambda(propAccess, paramExpr);
  23. return expr;
  24. }
  25. /// <summary>
  26. /// 多个OrderBy用逗号隔开,属性前面带-号表示反序排序,exp:"name,-createtime"
  27. /// </summary>
  28. /// <typeparam name="T"></typeparam>
  29. /// <param name="query"></param>
  30. /// <param name="name"></param>
  31. /// <returns></returns>
  32. public static IEnumerable<T> OrderByBatch<T>(this IEnumerable<T> query, string name) {
  33. var index = 0;
  34. var a = name.Split(',');
  35. foreach (var item in a) {
  36. var m = index++ > 0 ? "ThenBy" : "OrderBy";
  37. if (item.StartsWith("-")) {
  38. m += "Descending";
  39. name = item.Substring(1);
  40. } else {
  41. name = item;
  42. }
  43. name = name.Trim();
  44.  
  45. var propInfo = GetPropertyInfo(typeof(T), name);
  46. var expr = GetOrderExpression(typeof(T), propInfo);
  47. var method = typeof(Enumerable).GetMethods().FirstOrDefault(mt => mt.Name == m && mt.GetParameters().Length == 2);
  48. var genericMethod = method.MakeGenericMethod(typeof(T), propInfo.PropertyType);
  49. query = (IEnumerable<T>)genericMethod.Invoke(null, new object[] { query, expr.Compile() });
  50. }
  51. return query;
  52. }
  53.  
  54. /// <summary>
  55. /// 多个OrderBy用逗号隔开,属性前面带-号表示反序排序,exp:"name,-createtime"
  56. /// </summary>
  57. /// <typeparam name="T"></typeparam>
  58. /// <param name="query"></param>
  59. /// <param name="name"></param>
  60. /// <returns></returns>
  61. public static IQueryable<T> OrderByBatch<T>(this IQueryable<T> query, string name)
  62. {
  63. var index = 0;
  64. var a = name.Split(',');
  65. foreach (var item in a)
  66. {
  67. var m = index++ > 0 ? "ThenBy" : "OrderBy";
  68. if (item.StartsWith("-"))
  69. {
  70. m += "Descending";
  71. name = item.Substring(1);
  72. }
  73. else
  74. {
  75. name = item;
  76. }
  77. name = name.Trim();
  78.  
  79. var propInfo = GetPropertyInfo(typeof(T), name);
  80. var expr = GetOrderExpression(typeof(T), propInfo);
  81. var method = typeof(Queryable).GetMethods().FirstOrDefault(mt => mt.Name == m && mt.GetParameters().Length == 2);
  82. var genericMethod = method.MakeGenericMethod(typeof(T), propInfo.PropertyType);
  83. query = (IQueryable<T>)genericMethod.Invoke(null, new object[] { query, expr });
  84. }
  85. return query;
  86. }
  87.  
  88. /// <summary>
  89. /// 正序排序单个
  90. /// </summary>
  91. /// <typeparam name="T"></typeparam>
  92. /// <param name="query"></param>
  93. /// <param name="name"></param>
  94. /// <returns></returns>
  95. public static IQueryable<T> OrderBy<T>(this IQueryable<T> query, string name) {
  96. var propInfo = GetPropertyInfo(typeof(T), name);
  97. var expr = GetOrderExpression(typeof(T), propInfo);
  98.  
  99. var method = typeof(Queryable).GetMethods().FirstOrDefault(m => m.Name == "OrderBy" && m.GetParameters().Length == 2);
  100. var genericMethod = method.MakeGenericMethod(typeof(T), propInfo.PropertyType);
  101. return (IQueryable<T>)genericMethod.Invoke(null, new object[] { query, expr });
  102. }
  103. /// <summary>
  104. /// 正序排序单个(非首个)
  105. /// </summary>
  106. /// <typeparam name="T"></typeparam>
  107. /// <param name="query"></param>
  108. /// <param name="name"></param>
  109. /// <returns></returns>
  110. public static IQueryable<T> ThenBy<T>(this IQueryable<T> query, string name)
  111. {
  112. var propInfo = GetPropertyInfo(typeof(T), name);
  113. var expr = GetOrderExpression(typeof(T), propInfo);
  114.  
  115. var method = typeof(Queryable).GetMethods().FirstOrDefault(m => m.Name == "ThenBy" && m.GetParameters().Length == 2);
  116. var genericMethod = method.MakeGenericMethod(typeof(T), propInfo.PropertyType);
  117. return (IQueryable<T>)genericMethod.Invoke(null, new object[] { query, expr });
  118. }
  119. /// <summary>
  120. /// 反序排序单个
  121. /// </summary>
  122. /// <typeparam name="T"></typeparam>
  123. /// <param name="query"></param>
  124. /// <param name="name"></param>
  125. /// <returns></returns>
  126. public static IQueryable<T> OrderByDescending<T>(this IQueryable<T> query, string name)
  127. {
  128. var propInfo = GetPropertyInfo(typeof(T), name);
  129. var expr = GetOrderExpression(typeof(T), propInfo);
  130. var metMethods = typeof(Queryable).GetMethods();
  131. var method = metMethods.FirstOrDefault(m => m.Name == "OrderByDescending" && m.GetParameters().Length == 2);
  132. var genericMethod = method.MakeGenericMethod(typeof(T), propInfo.PropertyType);
  133. return (IQueryable<T>)genericMethod.Invoke(null, new object[] { query, expr });
  134. }
  135. /// <summary>
  136. /// 反序排序单个(非首个)
  137. /// </summary>
  138. /// <typeparam name="T"></typeparam>
  139. /// <param name="query"></param>
  140. /// <param name="name"></param>
  141. /// <returns></returns>
  142. public static IQueryable<T> ThenByDescending<T>(this IQueryable<T> query, string name)
  143. {
  144. var propInfo = GetPropertyInfo(typeof(T), name);
  145. var expr = GetOrderExpression(typeof(T), propInfo);
  146. var metMethods = typeof(Queryable).GetMethods();
  147. var method = metMethods.FirstOrDefault(m => m.Name == "ThenByDescending" && m.GetParameters().Length == 2);
  148. var genericMethod = method.MakeGenericMethod(typeof(T), propInfo.PropertyType);
  149. return (IQueryable<T>)genericMethod.Invoke(null, new object[] { query, expr });
  150. }
  151. }
  152. }

以及分页支持类PageData<T>

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4.  
  5. namespace Repository
  6. {
  7. public class PageData<T>
  8. {
  9. public List<T> Rows { get; set; }
  10. public long Totals { get; set; }
  11. }
  12. }

2.3构造RepositoryBase<T>

  1. using Entity;
  2. using Microsoft.EntityFrameworkCore;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Linq.Expressions;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. using Microsoft.EntityFrameworkCore.Infrastructure;
  10. using System.IO;
  11. using Entity.Table;
  12.  
  13. namespace Repository
  14. {
  15. public class RepositoryBase<T> where T : EntityBase
  16. {
  17. private readonly DbSet<T> _dbSet;
  18. public AeDbContext DbContext { get; } = null;
  19. public RepositoryBase(AeDbContext context)
  20. {
  21. DbContext = context;
  22. _dbSet = DbContext.Set<T>();
  23. }
  24. public DatabaseFacade Database => DbContext.Database;
  25. public IQueryable<T> Entities => _dbSet.AsQueryable().AsNoTracking();
  26. public int SaveChanges()
  27. {
  28. return DbContext.SaveChanges();
  29. }
  30. public async Task<int> SaveChangesAsync()
  31. {
  32. return await DbContext.SaveChangesAsync();
  33. }
  34. public bool Any(Expression<Func<T, bool>> whereLambd)
  35. {
  36. return _dbSet.Where(whereLambd).Any();
  37. }
  38. public void Disposed()
  39. {
  40. throw new Exception("不允许在这里释放上下文,请在UnitOfWork中操作");
  41. //DbContext.Dispose();
  42. }
  43.  
  44. #region 插入数据
  45. public bool Insert(T entity, bool isSaveChange = true)
  46. {
  47. _dbSet.Add(entity);
  48. if (isSaveChange)
  49. {
  50. return SaveChanges() > 0;
  51. }
  52. return false;
  53. }
  54. public async Task<bool> InsertAsync(T entity, bool isSaveChange = true)
  55. {
  56. _dbSet.Add(entity);
  57. if (isSaveChange)
  58. {
  59. return await SaveChangesAsync() > 0;
  60. }
  61. return false;
  62. }
  63. public bool Insert(List<T> entitys, bool isSaveChange = true)
  64. {
  65. _dbSet.AddRange(entitys);
  66. if (isSaveChange)
  67. {
  68. return SaveChanges() > 0;
  69. }
  70. return false;
  71. }
  72. public async Task<bool> InsertAsync(List<T> entitys, bool isSaveChange = true)
  73. {
  74. _dbSet.AddRange(entitys);
  75. if (isSaveChange)
  76. {
  77. return await SaveChangesAsync() > 0;
  78. }
  79. return false;
  80. }
  81. #endregion
  82.  
  83. #region 删除
  84. public bool Delete(T entity, bool isSaveChange = true)
  85. {
  86. _dbSet.Attach(entity);
  87. _dbSet.Remove(entity);
  88. return isSaveChange ? SaveChanges() > 0 : false;
  89. }
  90. public bool Delete(List<T> entitys, bool isSaveChange = true)
  91. {
  92. entitys.ForEach(entity =>
  93. {
  94. _dbSet.Attach(entity);
  95. _dbSet.Remove(entity);
  96. });
  97. return isSaveChange ? SaveChanges() > 0 : false;
  98. }
  99.  
  100. public virtual async Task<bool> DeleteAsync(T entity, bool isSaveChange = true)
  101. {
  102.  
  103. _dbSet.Attach(entity);
  104. _dbSet.Remove(entity);
  105. return isSaveChange ? await SaveChangesAsync() > 0 : false;
  106. }
  107. public virtual async Task<bool> DeleteAsync(List<T> entitys, bool isSaveChange = true)
  108. {
  109. entitys.ForEach(entity =>
  110. {
  111. _dbSet.Attach(entity);
  112. _dbSet.Remove(entity);
  113. });
  114. return isSaveChange ? await SaveChangesAsync() > 0 : false;
  115. }
  116. #endregion
  117.  
  118. #region 更新数据
  119. public bool Update(T entity, bool isSaveChange = true, List<string> updatePropertyList = null, bool modified = true)
  120. {
  121. if (entity == null)
  122. {
  123. return false;
  124. }
  125. _dbSet.Attach(entity);
  126. var entry = DbContext.Entry(entity);
  127. if (updatePropertyList == null)
  128. {
  129. entry.State = EntityState.Modified;//全字段更新
  130. }
  131. else
  132. {
  133. if (modified)
  134. {
  135. updatePropertyList.ForEach(c => {
  136. entry.Property(c).IsModified = true; //部分字段更新的写法
  137. });
  138. }
  139. else
  140. {
  141. entry.State = EntityState.Modified;//全字段更新
  142. updatePropertyList.ForEach(c => {
  143. entry.Property(c).IsModified = false; //部分字段不更新的写法
  144. });
  145. }
  146. }
  147. if (isSaveChange)
  148. {
  149. return SaveChanges() > 0;
  150. }
  151. return false;
  152. }
  153. public bool Update(List<T> entitys, bool isSaveChange = true)
  154. {
  155. if (entitys == null || entitys.Count == 0)
  156. {
  157. return false;
  158. }
  159. entitys.ForEach(c => {
  160. Update(c, false);
  161. });
  162. if (isSaveChange)
  163. {
  164. return SaveChanges() > 0;
  165. }
  166. return false;
  167. }
  168. public async Task<bool> UpdateAsync(T entity, bool isSaveChange = true, List<string> updatePropertyList = null, bool modified = true)
  169. {
  170. if (entity == null)
  171. {
  172. return false;
  173. }
  174. _dbSet.Attach(entity);
  175. var entry = DbContext.Entry<T>(entity);
  176. if (updatePropertyList == null)
  177. {
  178. entry.State = EntityState.Modified;//全字段更新
  179. }
  180. else
  181. {
  182. if (modified)
  183. {
  184. updatePropertyList.ForEach(c => {
  185. entry.Property(c).IsModified = true; //部分字段更新的写法
  186. });
  187. }
  188. else
  189. {
  190. entry.State = EntityState.Modified;//全字段更新
  191. updatePropertyList.ForEach(c => {
  192. entry.Property(c).IsModified = false; //部分字段不更新的写法
  193. });
  194. }
  195. }
  196. if (isSaveChange)
  197. {
  198. return await SaveChangesAsync() > 0;
  199. }
  200. return false;
  201. }
  202. public async Task<bool> UpdateAsync(List<T> entitys, bool isSaveChange = true)
  203. {
  204. if (entitys == null || entitys.Count == 0)
  205. {
  206. return false;
  207. }
  208. entitys.ForEach(c => {
  209. _dbSet.Attach(c);
  210. DbContext.Entry<T>(c).State = EntityState.Modified;
  211. });
  212. if (isSaveChange)
  213. {
  214. return await SaveChangesAsync() > 0;
  215. }
  216. return false;
  217. }
  218. #endregion
  219.  
  220. #region 查找
  221. public long Count(Expression<Func<T, bool>> predicate = null)
  222. {
  223. if (predicate == null)
  224. {
  225. predicate = c => true;
  226. }
  227. return _dbSet.LongCount(predicate);
  228. }
  229. public async Task<long> CountAsync(Expression<Func<T, bool>> predicate = null)
  230. {
  231. if (predicate == null)
  232. {
  233. predicate = c => true;
  234. }
  235. return await _dbSet.LongCountAsync(predicate);
  236. }
  237. public T Get(object id)
  238. {
  239. if (id == null)
  240. {
  241. return default(T);
  242. }
  243. return _dbSet.Find(id);
  244. }
  245. public T Get(Expression<Func<T, bool>> predicate = null, bool isNoTracking = true)
  246. {
  247. var data = isNoTracking ? _dbSet.Where(predicate).AsNoTracking() : _dbSet.Where(predicate);
  248. return data.FirstOrDefault();
  249. }
  250. public async Task<T> GetAsync(object id)
  251. {
  252. if (id == null)
  253. {
  254. return default(T);
  255. }
  256. return await _dbSet.FindAsync(id);
  257. }
  258. public async Task<T> GetAsync(Expression<Func<T, bool>> predicate = null, bool isNoTracking = true)
  259. {
  260. var data = isNoTracking ? _dbSet.Where(predicate).AsNoTracking() : _dbSet.Where(predicate);
  261. return await data.FirstOrDefaultAsync();
  262. }
  263. public async Task<List<T>> GetListAsync(Expression<Func<T, bool>> predicate = null, string ordering = "", bool isNoTracking = true)
  264. {
  265. var data = isNoTracking ? _dbSet.Where(predicate).AsNoTracking() : _dbSet.Where(predicate);
  266. if (!string.IsNullOrEmpty(ordering))
  267. {
  268. data = data.OrderByBatch(ordering);
  269. }
  270. return await data.ToListAsync();
  271. }
  272. public List<T> GetList(Expression<Func<T, bool>> predicate = null, string ordering = "", bool isNoTracking = true)
  273. {
  274. var data = isNoTracking ? _dbSet.Where(predicate).AsNoTracking() : _dbSet.Where(predicate);
  275. if (!string.IsNullOrEmpty(ordering))
  276. {
  277. data = data.OrderByBatch(ordering);
  278. }
  279. return data.ToList();
  280. }
  281. public async Task<IQueryable<T>> LoadAsync(Expression<Func<T, bool>> predicate = null, bool isNoTracking = true)
  282. {
  283. if (predicate == null)
  284. {
  285. predicate = c => true;
  286. }
  287. return await Task.Run(() => isNoTracking ? _dbSet.Where(predicate).AsNoTracking() : _dbSet.Where(predicate));
  288. }
  289. public IQueryable<T> Load(Expression<Func<T, bool>> predicate = null, bool isNoTracking = true)
  290. {
  291. if (predicate == null)
  292. {
  293. predicate = c => true;
  294. }
  295. return isNoTracking ? _dbSet.Where(predicate).AsNoTracking() : _dbSet.Where(predicate);
  296. }
  297. #region 分页查找
  298. /// <summary>
  299. /// 分页查询异步
  300. /// </summary>
  301. /// <param name="whereLambda">查询添加(可有,可无)</param>
  302. /// <param name="ordering">排序条件(一定要有)</param>
  303. /// <param name="pageIndex">当前页码</param>
  304. /// <param name="pageSize">每页大小</param>
  305. /// <param name="isOrder">排序正反</param>
  306. /// <returns></returns>
  307. public async Task<PageData<T>> GetPageAsync<TKey>(Expression<Func<T, bool>> whereLambda, Expression<Func<T, TKey>> orderBy, int pageIndex, int pageSize, bool isOrder = true, bool isNoTracking = true)
  308. {
  309. IQueryable<T> data = isOrder ?
  310. _dbSet.OrderBy(orderBy) :
  311. _dbSet.OrderByDescending(orderBy);
  312.  
  313. if (whereLambda != null)
  314. {
  315. data = isNoTracking ? data.Where(whereLambda).AsNoTracking() : data.Where(whereLambda);
  316. }
  317. PageData<T> pageData = new PageData<T>
  318. {
  319. Totals = await data.CountAsync(),
  320. Rows = await data.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync()
  321. };
  322. return pageData;
  323. }
  324.  
  325. /// <summary>
  326. /// 分页查询异步
  327. /// </summary>
  328. /// <param name="whereLambda">查询添加(可有,可无)</param>
  329. /// <param name="ordering">排序条件(一定要有,多个用逗号隔开,倒序开头用-号)</param>
  330. /// <param name="pageIndex">当前页码</param>
  331. /// <param name="pageSize">每页大小</param>
  332. /// <returns></returns>
  333. public async Task<PageData<T>> GetPageAsync(Expression<Func<T, bool>> whereLambda, string ordering, int pageIndex, int pageSize, bool isNoTracking = true)
  334. {
  335. // 分页 一定注意: Skip 之前一定要 OrderBy
  336. if (string.IsNullOrEmpty(ordering))
  337. {
  338. ordering = nameof(T) + "Id";//默认以Id排序
  339. }
  340. var data = _dbSet.OrderByBatch(ordering);
  341. if (whereLambda != null)
  342. {
  343. data = isNoTracking ? data.Where(whereLambda).AsNoTracking() : data.Where(whereLambda);
  344. }
  345. //查看生成的sql,找到大数据下分页巨慢原因为order by 耗时
  346. //var sql = data.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToSql();
  347. //File.WriteAllText(@"D:\sql.txt",sql);
  348. PageData<T> pageData = new PageData<T>
  349. {
  350. Totals = await data.CountAsync(),
  351. Rows = await data.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync()
  352. };
  353. return pageData;
  354. }
  355.  
  356. /// <summary>
  357. /// 分页查询
  358. /// </summary>
  359. /// <param name="whereLambda">查询添加(可有,可无)</param>
  360. /// <param name="ordering">排序条件(一定要有,多个用逗号隔开,倒序开头用-号)</param>
  361. /// <param name="pageIndex">当前页码</param>
  362. /// <param name="pageSize">每页大小</param>
  363. /// <returns></returns>
  364. public PageData<T> GetPage(Expression<Func<T, bool>> whereLambda, string ordering, int pageIndex, int pageSize, bool isNoTracking = true)
  365. {
  366. // 分页 一定注意: Skip 之前一定要 OrderBy
  367. if (string.IsNullOrEmpty(ordering))
  368. {
  369. ordering = nameof(T) + "Id";//默认以Id排序
  370. }
  371. var data = _dbSet.OrderByBatch(ordering);
  372. if (whereLambda != null)
  373. {
  374. data = isNoTracking ? data.Where(whereLambda).AsNoTracking() : data.Where(whereLambda);
  375. }
  376. PageData<T> pageData = new PageData<T>
  377. {
  378. Totals = data.Count(),
  379. Rows = data.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList()
  380. };
  381. return pageData;
  382. }
  383. #endregion
  384. #endregion
  385. }
  386. }

2.4添加Table文件夹,添加SysUserRepository

  1. using Entity;
  2. using Entity.Table;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Text;
  6.  
  7. namespace Repository.Table
  8. {
  9. public class SysUserRepository : RepositoryBase<SysUser>
  10. {
  11. public SysUserRepository(AeDbContext context) : base(context)
  12. {
  13. }
  14. }
  15. }

2.5添加工作单元UnitOfWork

  1. using Entity;
  2. using Microsoft.EntityFrameworkCore;
  3. using Microsoft.EntityFrameworkCore.Storage;
  4. using Repository.Table;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Data;
  8. using System.Linq;
  9. using System.Text;
  10. using System.Threading.Tasks;
  11.  
  12. namespace Repository
  13. {
  14. public class UnitOfWork : IDisposable
  15. {
  16. public static UnitOfWork Instance = new UnitOfWork(new AeDbContext());
  17. public AeDbContext DbContext { get; set; } = null;
  18. public UnitOfWork(AeDbContext dbContext)
  19. {
  20. DbContext = dbContext;
  21. }
  22.  
  23. #region 字段
  24. private SysUserRepository _SysUserRepository = null;
  25. #endregion
  26.  
  27. #region 操作类属性
  28. public SysUserRepository SysUserRepository => _SysUserRepository ?? (_SysUserRepository = new SysUserRepository(DbContext));
  29. #endregion
  30.  
  31. #region 仓储操作(提交事务保存SaveChanges(),回滚RollBackChanges(),释放资源Dispose())
  32. /// <summary>
  33. /// 保存
  34. /// </summary>
  35. public int SaveChanges()
  36. {
  37. return DbContext.SaveChanges();
  38. }
  39. public async Task<int> SaveChangesAsync()
  40. {
  41. return await DbContext.SaveChangesAsync();
  42. }
  43. /// <summary>
  44. /// 回滚
  45. /// </summary>
  46. public void RollBackChanges()
  47. {
  48. var items = DbContext.ChangeTracker.Entries().ToList();
  49. items.ForEach(o => o.State = EntityState.Unchanged);
  50. }
  51. /// <summary>
  52. /// 释放资源
  53. /// </summary>
  54. private bool disposed = false;
  55.  
  56. protected virtual void Dispose(bool disposing)
  57. {
  58. if (!this.disposed)
  59. {
  60. if (disposing)
  61. {
  62. DbContext.Dispose();//随着工作单元的销毁而销毁
  63. }
  64. }
  65. this.disposed = true;
  66. }
  67. public void Dispose()
  68. {
  69. Dispose(true);
  70. GC.SuppressFinalize(this);
  71. }
  72. public IDbContextTransaction BeginTransaction()
  73. {
  74. var scope = DbContext.Database.BeginTransaction();
  75. return scope;
  76. }
  77. #endregion
  78. }
  79. }

这样仓储层就构造完成了,篇幅已经很长了,Service层就先不介绍了。

3.WebApi项目测试

新建.NetCore的项目的Web应用程序ApiTest,选择webapi方式,并引用Entity和Repository项目

3.1注入EF

  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. services.AddMvc();
  4. services.AddDbContext<AeDbContext>(options => options.UseMySql(Configuration.GetConnectionString("SQLConnection")));
           services.AddTransient(typeof(UnitOfWork));//注入工作单元
  5. }

3.2测试

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Threading.Tasks;
  5. using Microsoft.AspNetCore.Mvc;
  6. using Repository;
  7.  
  8. namespace ApiTest.Controllers
  9. {
  10. [Route("api/[controller]")]
  11. public class ValuesController : Controller
  12. {
  13. UnitOfWork _unitOfWork;
  14. public ValuesController(UnitOfWork unitOfWork)
  15. {
  16. _unitOfWork = unitOfWork;
  17. }
  18. // GET api/values
  19. [HttpGet]
  20. public IEnumerable<string> Get()
  21. {
  22. var adminModel = _unitOfWork.SysUserRepository.Get("admin");
  23. if(adminModel == null)
  24. {
  25. adminModel = new Entity.Table.SysUser()
  26. {
  27. SysUserId = "admin",
  28. UserName = "admin",
  29. Password = "123456",
  30. UserType = "1",
  31. CreateTime = DateTime.Now,
  32. Status = 1,
  33. Sort = 0
  34. };
  35. _unitOfWork.SysUserRepository.Insert(adminModel);
  36. }
  37. return new List<string> { adminModel.UserName , adminModel.Password };
  38. }
  39.  
  40. // GET api/values/5
  41. [HttpGet("{id}")]
  42. public string Get(int id)
  43. {
  44. return "value";
  45. }
  46.  
  47. // POST api/values
  48. [HttpPost]
  49. public void Post([FromBody]string value)
  50. {
  51. }
  52.  
  53. // PUT api/values/5
  54. [HttpPut("{id}")]
  55. public void Put(int id, [FromBody]string value)
  56. {
  57. }
  58.  
  59. // DELETE api/values/5
  60. [HttpDelete("{id}")]
  61. public void Delete(int id)
  62. {
  63. }
  64. }
  65. }

测试结果:

仓储分页查询 测试

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Threading.Tasks;
  5. using Entity.Table;
  6. using LinqKit;
  7. using Microsoft.AspNetCore.Http;
  8. using Microsoft.AspNetCore.Mvc;
  9. using Repository;
  10.  
  11. namespace ApiTest.Controllers
  12. {
  13. [Produces("application/json")]
  14. [Route("api/[controller]")]
  15. public class QueryPageController : Controller
  16. {
  17. UnitOfWork _unitOfWork;
  18. public QueryPageController(UnitOfWork unitOfWork)
  19. {
  20. _unitOfWork = unitOfWork;
  21. }
  22. /// <summary>
  23. /// 分页测试
  24. /// </summary>
  25. /// <returns></returns>
  26. [HttpGet]
  27. public async Task<IActionResult> Get(string userName,int? Status,string NikeName)
  28. {
  29. var userCount = _unitOfWork.SysUserRepository.Count();
  30. if(userCount < 100)
  31. {
  32. await CreateUser(100);
  33. }
  34. //获取分页数据方式1
  35. //获取用户名包含user的排序根据Sort正序,CreateTime倒序排序的第1页的20条数据
  36. var pageModel = await _unitOfWork.SysUserRepository.GetPageAsync(o => o.UserName.Contains("user") && o.Status == 1, "Sort,-CreateTime", 1, 20);
  37.  
  38. //获取分页数据方式2
  39. //使用PredicateBuilder获取分页数据方式支持筛选
  40. var predicate = PredicateBuilder.New<SysUser>(true);//查询条件,推荐后台使用这种方式灵活筛选
  41. #region 添加条件查询
  42. if (!string.IsNullOrEmpty(userName))
  43. {
  44. predicate = predicate.And(i => i.UserName.Contains(userName));
  45. }
  46. if (Status != null)
  47. {
  48. predicate = predicate.And(i => i.Status.Equals(Status));
  49. }
  50. if (!string.IsNullOrEmpty(NikeName))
  51. {
  52. predicate = predicate.And(i => i.NikeName.Equals(NikeName));
  53. }
  54. #endregion
  55. var pageModel1 = await _unitOfWork.SysUserRepository.GetPageAsync(predicate, "Sort,-CreateTime", 1, 20);
  56. return Json(new { pageModel, pageModel1 });
  57. }
  58. /// <summary>
  59. /// 构造数据
  60. /// </summary>
  61. /// <param name="count"></param>
  62. /// <returns></returns>
  63. public async Task<bool> CreateUser(int count = 1)
  64. {
  65. List<SysUser> inserUsers = new List<SysUser>();
  66. for (int i = 0; i < count; i++)
  67. {
  68. inserUsers.Add(new SysUser
  69. {
  70. SysUserId = Guid.NewGuid().ToString("N"),
  71. UserName = $"user{i}",
  72. Password = "123456",
  73. UserType = "0",
  74. CreateTime = DateTime.Now,
  75. Status = 1,
  76. Sort = 0
  77. });
  78. }
  79. return await _unitOfWork.SysUserRepository.InsertAsync(inserUsers);
  80. }
  81.  
  82. }
  83. }

结果:

4.Github项目地址

最后附上github源码:https://github.com/atorzhang/EFTest

注:原创,转载请注明出处,原文地址:https://www.cnblogs.com/jomzhang/p/10245077.html

 
分类: asp.net core

EFCore+Mysql仓储层建设(分页、多字段排序、部分字段更新)的更多相关文章

  1. angularjs 字段排序 多字段排序

    我们用angularjs {{}},ng-model循环绑定数组或对象的内容的时候,有时候会用到排序,有时候可能会有多个字段排序 具体要用到过滤 数据的展现,可以通过ng-repeat实现.当网页解析 ...

  2. C# List根据某一字段排序 将字段相同的排序到一起

    List<JZJLXQ_Prescription_Item> ciList = new List<JZJLXQ_Prescription_Item>(); List<JZ ...

  3. .netCore+Vue 搭建的简捷开发框架 (2)--仓储层实现和EFCore 的使用

    书接上文,继续搭建我们基于.netCore 的开发框架.首先是我们的项目分层结构. 这个分层结构,是参考张老师的分层结构,但是实际项目中,我没有去实现仓储模型.因为我使用的是EFCore ,最近也一直 ...

  4. abp(net core)+easyui+efcore实现仓储管理系统——展现层实现增删改查之列表视图(七)

    abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统——ABP总体介绍(一) abp(net core)+ ...

  5. Oracle、MySql、SQLServer 数据分页查询

    最近简单的对oracle,mysql,sqlserver2005的数据分页查询作了研究,把各自的查询的语句贴出来供大家学习..... (一). mysql的分页查询 mysql的分页查询是最简单的,借 ...

  6. MySQL 百万级分页优化(Mysql千万级快速分页)

    以下分享一点我的经验 一般刚开始学SQL的时候,会这样写 : SELECT * FROM table ORDER BY id LIMIT 1000, 10; 但在数据达到百万级的时候,这样写会慢死 : ...

  7. 2019年7月16日 abp(net core)+easyui+efcore实现仓储管理系统——多语言(十)

    abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统——ABP总体介绍(一) abp(net core)+ ...

  8. abp(net core)+easyui+efcore实现仓储管理系统——使用 WEBAPI实现CURD (十一)

    abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统——ABP总体介绍(一) abp(net core)+ ...

  9. abp(net core)+easyui+efcore实现仓储管理系统——使用 WEBAPI实现CURD (十二)

    abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统——ABP总体介绍(一) abp(net core)+ ...

随机推荐

  1. java 11 实现RFC7539中指定的ChaCha20和Poly1305两种加密算法, 代替RC4

    实现 RFC 7539的ChaCha20 and ChaCha20-Poly1305加密算法 RFC7748定义的秘钥协商方案更高效, 更安全. JDK增加两个新的接口 XECPublicKey 和 ...

  2. Django web编程1 -- 创建项目和应用

    python:3.7.2 Django:2.1.7 1.创建虚拟环境 虚拟环境是系统的一个位置,可以在其中安装包,并将其与其他python包隔离. 创建目录,命名为learning_log,并切换到这 ...

  3. ArcGis dbf读写——挂接Excel到属性表 C#

    ArcMap提供了挂接Excel表格信息到属性表的功能,但是当数据量较大到以万计甚至十万计的时候这个功能就歇菜了,当然,你可以考虑分段挂接.这个挂接功能只是做了一个表关联,属性记录每个字段的信息需要通 ...

  4. i-83.net quadhost子产品

    i-83.net 6$一年, 首年半价, 优惠码: APR19-NAT50 加拿大 ---------------------------------------------------------- ...

  5. CSS3 Background-origin

    Background-origin是CSS3为Background扩展的第三个属性,从Background-origin字面上不难发现是指背景图片的原点,其实background-origin主要就是 ...

  6. 推送提交(git push)

    当需要同别人共享某个分支上的工作成果时,就要把它推送到一个具有写权限的远程仓库.你的本地分支并不会自动同步到远程仓库,必须要显式地推送那些你想要与别人共享的分支.这样一来,你可以使用私有分支做一些不想 ...

  7. go 【第二篇】包、变量、函数

    包 初试 每个 Go 程序都是由包组成的. 程序运行的入口是包 `main`. 这个程序使用并导入了包 "fmt" 和 `"math/rand"`. 按照惯例, ...

  8. C# Dictionary 泛型

    Dictionary<string, string>是一个泛型,什么是泛型? 使用泛型下面是用泛型来重写上面的栈,用一个通用的数据类型T来作为一个占位符,等待在实例化时用一个实际的类型来代 ...

  9. springmvc 开发流程图

  10. LVS节点健康检查及管理脚本

    在LVS负载均衡主节点上,模拟keepalived健康检查功能管理LVS节点,当节点挂掉从服务器池中剔除,好了再加到服务器池中来. 工具:yum install -y ipvsadm web03:10 ...