DbContextExt.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. using Microsoft.EntityFrameworkCore;
  2. using System;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Linq.Expressions;
  7. using System.Text;
  8. using System.Transactions;
  9. namespace Masuit.Tools.Core
  10. {
  11. public static class DbContextExt
  12. {
  13. /// <summary>
  14. /// 获取变化的实体信息
  15. /// </summary>
  16. /// <typeparam name="T"></typeparam>
  17. /// <param name="db"></param>
  18. /// <returns></returns>
  19. public static IEnumerable<ChangeEntry> GetChanges<T>(this DbContext db)
  20. {
  21. return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Modified && e.Entity is T).Select(e =>
  22. {
  23. var originalObject = e.OriginalValues.ToObject();
  24. var currentObject = e.CurrentValues.ToObject();
  25. return new ChangeEntry
  26. {
  27. EntityState = e.State,
  28. Entity = e.Entity,
  29. EntityType = e.OriginalValues.EntityType.ClrType,
  30. ChangeProperties = e.OriginalValues.Properties.Select(p => (Property: p, Value: p.PropertyInfo.GetValue(originalObject))).Zip(e.CurrentValues.Properties.Select(p => (Property: p, Value: p.PropertyInfo.GetValue(currentObject))), (t1, t2) => new ChangePropertyInfo()
  31. {
  32. PropertyInfo = t1.Property.PropertyInfo,
  33. OriginalValue = t1.Value,
  34. CurrentValue = t2.Value,
  35. IsPrimaryKey = t1.Property.IsPrimaryKey(),
  36. IsForeignKey = t1.Property.IsForeignKey()
  37. }).Where(t => Comparer.Default.Compare(t.OriginalValue, t.CurrentValue) != 0).ToList()
  38. };
  39. });
  40. }
  41. /// <summary>
  42. /// 获取变化的实体信息
  43. /// </summary>
  44. /// <param name="db"></param>
  45. /// <returns></returns>
  46. public static IEnumerable<ChangeEntry> GetChanges(this DbContext db)
  47. {
  48. return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Modified).Select(e =>
  49. {
  50. var originalObject = e.OriginalValues.ToObject();
  51. var currentObject = e.CurrentValues.ToObject();
  52. return new ChangeEntry()
  53. {
  54. EntityState = e.State,
  55. Entity = e.Entity,
  56. EntityType = e.OriginalValues.EntityType.ClrType,
  57. ChangeProperties = e.OriginalValues.Properties.Select(p => (Property: p, Value: p.PropertyInfo.GetValue(originalObject))).Zip(e.CurrentValues.Properties.Select(p => (Property: p, Value: p.PropertyInfo.GetValue(currentObject))), (t1, t2) => new ChangePropertyInfo()
  58. {
  59. PropertyInfo = t1.Property.PropertyInfo,
  60. OriginalValue = t1.Value,
  61. CurrentValue = t2.Value,
  62. IsPrimaryKey = t1.Property.IsPrimaryKey(),
  63. IsForeignKey = t1.Property.IsForeignKey(),
  64. }).Where(t => Comparer.Default.Compare(t.OriginalValue, t.CurrentValue) != 0).ToList()
  65. };
  66. });
  67. }
  68. /// <summary>
  69. /// 获取添加的实体信息
  70. /// </summary>
  71. /// <typeparam name="T"></typeparam>
  72. /// <param name="db"></param>
  73. /// <returns></returns>
  74. public static IEnumerable<ChangeEntry> GetAdded<T>(this DbContext db)
  75. {
  76. return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Added && e.Entity is T).Select(e =>
  77. {
  78. var currentObject = e.CurrentValues.ToObject();
  79. return new ChangeEntry
  80. {
  81. EntityState = e.State,
  82. Entity = e.Entity,
  83. EntityType = e.CurrentValues.EntityType.ClrType,
  84. ChangeProperties = e.CurrentValues.Properties.Select(p => new ChangePropertyInfo()
  85. {
  86. PropertyInfo = p.PropertyInfo,
  87. CurrentValue = p.PropertyInfo.GetValue(currentObject),
  88. IsPrimaryKey = p.IsPrimaryKey(),
  89. IsForeignKey = p.IsForeignKey(),
  90. }).ToList()
  91. };
  92. });
  93. }
  94. /// <summary>
  95. /// 获取添加的实体信息
  96. /// </summary>
  97. /// <param name="db"></param>
  98. /// <returns></returns>
  99. public static IEnumerable<ChangeEntry> GetAdded(this DbContext db)
  100. {
  101. return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Added).Select(e =>
  102. {
  103. var currentObject = e.CurrentValues.ToObject();
  104. return new ChangeEntry
  105. {
  106. EntityState = e.State,
  107. Entity = e.Entity,
  108. EntityType = e.CurrentValues.EntityType.ClrType,
  109. ChangeProperties = e.CurrentValues.Properties.Select(p => new ChangePropertyInfo()
  110. {
  111. PropertyInfo = p.PropertyInfo,
  112. CurrentValue = p.PropertyInfo.GetValue(currentObject),
  113. IsPrimaryKey = p.IsPrimaryKey(),
  114. IsForeignKey = p.IsForeignKey(),
  115. }).ToList()
  116. };
  117. });
  118. }
  119. /// <summary>
  120. /// 获取移除的实体信息
  121. /// </summary>
  122. /// <typeparam name="T"></typeparam>
  123. /// <param name="db"></param>
  124. /// <returns></returns>
  125. public static IEnumerable<ChangeEntry> GetRemoved<T>(this DbContext db)
  126. {
  127. return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Deleted && e.Entity is T).Select(e =>
  128. {
  129. var originalObject = e.OriginalValues.ToObject();
  130. return new ChangeEntry
  131. {
  132. EntityState = e.State,
  133. Entity = e.Entity,
  134. EntityType = e.OriginalValues.EntityType.ClrType,
  135. ChangeProperties = e.OriginalValues.Properties.Select(p => new ChangePropertyInfo()
  136. {
  137. PropertyInfo = p.PropertyInfo,
  138. OriginalValue = p.PropertyInfo.GetValue(originalObject),
  139. IsPrimaryKey = p.IsPrimaryKey(),
  140. IsForeignKey = p.IsForeignKey(),
  141. }).ToList()
  142. };
  143. });
  144. }
  145. /// <summary>
  146. /// 获取移除的实体信息
  147. /// </summary>
  148. /// <param name="db"></param>
  149. /// <returns></returns>
  150. public static IEnumerable<ChangeEntry> GetRemoved(this DbContext db)
  151. {
  152. return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Deleted).Select(e =>
  153. {
  154. var originalObject = e.OriginalValues.ToObject();
  155. return new ChangeEntry
  156. {
  157. EntityState = e.State,
  158. Entity = e.Entity,
  159. EntityType = e.OriginalValues.EntityType.ClrType,
  160. ChangeProperties = e.OriginalValues.Properties.Select(p => new ChangePropertyInfo()
  161. {
  162. PropertyInfo = p.PropertyInfo,
  163. OriginalValue = p.PropertyInfo.GetValue(originalObject),
  164. IsPrimaryKey = p.IsPrimaryKey(),
  165. IsForeignKey = p.IsForeignKey(),
  166. }).ToList()
  167. };
  168. });
  169. }
  170. /// <summary>
  171. /// 获取所有的变更信息
  172. /// </summary>
  173. /// <typeparam name="T"></typeparam>
  174. /// <param name="db"></param>
  175. /// <returns></returns>
  176. public static IEnumerable<ChangeEntry> GetAllChanges<T>(this DbContext db)
  177. {
  178. return GetChanges<T>(db).Union(GetAdded<T>(db)).Union(GetRemoved<T>(db));
  179. }
  180. /// <summary>
  181. /// 获取所有的变更信息
  182. /// </summary>
  183. /// <param name="db"></param>
  184. /// <returns></returns>
  185. public static IEnumerable<ChangeEntry> GetAllChanges(this DbContext db)
  186. {
  187. return GetChanges(db).Union(GetAdded(db)).Union(GetRemoved(db));
  188. }
  189. public static IQueryable<TEntity> IncludeRecursive<TEntity>(this IQueryable<TEntity> source, int levelIndex, Expression<Func<TEntity, ICollection<TEntity>>> expression) where TEntity : class
  190. {
  191. if (levelIndex < 0)
  192. throw new ArgumentOutOfRangeException(nameof(levelIndex));
  193. var member = (MemberExpression)expression.Body;
  194. var property = member.Member.Name;
  195. var sb = new StringBuilder();
  196. for (int i = 0; i < levelIndex; i++)
  197. {
  198. if (i > 0)
  199. sb.Append(Type.Delimiter);
  200. sb.Append(property);
  201. }
  202. return source.Include(sb.ToString());
  203. }
  204. public static async Task<List<T>> ToListWithNoLockAsync<T>(this IQueryable<T> query, CancellationToken cancellationToken = default)
  205. {
  206. using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions()
  207. {
  208. IsolationLevel = IsolationLevel.ReadUncommitted
  209. }, TransactionScopeAsyncFlowOption.Enabled);
  210. var result = await query.ToListAsync(cancellationToken);
  211. scope.Complete();
  212. return result;
  213. }
  214. public static async Task<int> CountWithNoLockAsync<T>(this IQueryable<T> query, CancellationToken cancellationToken = default)
  215. {
  216. using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions()
  217. {
  218. IsolationLevel = IsolationLevel.ReadUncommitted
  219. }, TransactionScopeAsyncFlowOption.Enabled);
  220. var result = await query.CountAsync(cancellationToken);
  221. scope.Complete();
  222. return result;
  223. }
  224. public static async Task<int> CountWithNoLockAsync<T>(this IQueryable<T> query, Expression<Func<T, bool>> where, CancellationToken cancellationToken = default)
  225. {
  226. using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions()
  227. {
  228. IsolationLevel = IsolationLevel.ReadUncommitted
  229. }, TransactionScopeAsyncFlowOption.Enabled);
  230. var result = await query.CountAsync(where, cancellationToken);
  231. scope.Complete();
  232. return result;
  233. }
  234. public static async Task<bool> AnyWithNoLockAsync<T>(this IQueryable<T> query, CancellationToken cancellationToken = default)
  235. {
  236. using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions()
  237. {
  238. IsolationLevel = IsolationLevel.ReadUncommitted
  239. }, TransactionScopeAsyncFlowOption.Enabled);
  240. var result = await query.AnyAsync(cancellationToken);
  241. scope.Complete();
  242. return result;
  243. }
  244. public static async Task<bool> AnyWithNoLockAsync<T>(this IQueryable<T> query, Expression<Func<T, bool>> where, CancellationToken cancellationToken = default)
  245. {
  246. using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions()
  247. {
  248. IsolationLevel = IsolationLevel.ReadUncommitted
  249. }, TransactionScopeAsyncFlowOption.Enabled);
  250. var result = await query.AnyAsync(where, cancellationToken);
  251. scope.Complete();
  252. return result;
  253. }
  254. public static async Task<T> FirstOrDefaultWithNoLockAsync<T>(this IQueryable<T> query, Expression<Func<T, bool>> where, CancellationToken cancellationToken = default)
  255. {
  256. using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions()
  257. {
  258. IsolationLevel = IsolationLevel.ReadUncommitted
  259. }, TransactionScopeAsyncFlowOption.Enabled);
  260. var result = await query.FirstOrDefaultAsync(where, cancellationToken);
  261. scope.Complete();
  262. return result;
  263. }
  264. public static async Task<T> FirstOrDefaultWithNoLockAsync<T>(this IQueryable<T> query, CancellationToken cancellationToken = default)
  265. {
  266. using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions()
  267. {
  268. IsolationLevel = IsolationLevel.ReadUncommitted
  269. }, TransactionScopeAsyncFlowOption.Enabled);
  270. var result = await query.FirstOrDefaultAsync(cancellationToken);
  271. scope.Complete();
  272. return result;
  273. }
  274. public static async Task<T> SingleOrDefaultWithNoLockAsync<T>(this IQueryable<T> query, Expression<Func<T, bool>> where, CancellationToken cancellationToken = default)
  275. {
  276. using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions()
  277. {
  278. IsolationLevel = IsolationLevel.ReadUncommitted
  279. }, TransactionScopeAsyncFlowOption.Enabled);
  280. var result = await query.SingleOrDefaultAsync(where, cancellationToken);
  281. scope.Complete();
  282. return result;
  283. }
  284. public static async Task<T> SingleOrDefaultWithNoLockAsync<T>(this IQueryable<T> query, CancellationToken cancellationToken = default)
  285. {
  286. using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions()
  287. {
  288. IsolationLevel = IsolationLevel.ReadUncommitted
  289. }, TransactionScopeAsyncFlowOption.Enabled);
  290. var result = await query.SingleOrDefaultAsync(cancellationToken);
  291. scope.Complete();
  292. return result;
  293. }
  294. public static async Task<bool> AllWithNoLockAsync<T>(this IQueryable<T> query, Expression<Func<T, bool>> where, CancellationToken cancellationToken = default)
  295. {
  296. using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions()
  297. {
  298. IsolationLevel = IsolationLevel.ReadUncommitted
  299. }, TransactionScopeAsyncFlowOption.Enabled);
  300. var result = await query.AllAsync(where, cancellationToken);
  301. scope.Complete();
  302. return result;
  303. }
  304. public static T NoLock<T, TDbContext>(this TDbContext dbContext, Func<TDbContext, T> func) where TDbContext : DbContext
  305. {
  306. var strategy = dbContext.Database.CreateExecutionStrategy();
  307. return strategy.Execute(() =>
  308. {
  309. var transactionOptions = new TransactionOptions
  310. {
  311. IsolationLevel = IsolationLevel.ReadUncommitted
  312. };
  313. using var scope = new TransactionScope(TransactionScopeOption.Required, transactionOptions);
  314. var result = func(dbContext);
  315. scope.Complete();
  316. return result;
  317. });
  318. }
  319. public static Task<T> NoLock<T, TDbContext>(this TDbContext dbContext, Func<TDbContext, Task<T>> func) where TDbContext : DbContext
  320. {
  321. var strategy = dbContext.Database.CreateExecutionStrategy();
  322. return strategy.ExecuteAsync(async () =>
  323. {
  324. var transactionOptions = new TransactionOptions
  325. {
  326. IsolationLevel = IsolationLevel.ReadUncommitted
  327. };
  328. using var scope = new TransactionScope(TransactionScopeOption.Required, transactionOptions);
  329. var result = await func(dbContext);
  330. scope.Complete();
  331. return result;
  332. });
  333. }
  334. public static T ExecutionStrategy<T, TDbContext>(this TDbContext dbContext, Func<TDbContext, T> func) where TDbContext : DbContext
  335. {
  336. var strategy = dbContext.Database.CreateExecutionStrategy();
  337. return strategy.Execute(() => func(dbContext));
  338. }
  339. public static Task<T> ExecutionStrategy<T, TDbContext>(this TDbContext dbContext, Func<TDbContext, Task<T>> func) where TDbContext : DbContext
  340. {
  341. var strategy = dbContext.Database.CreateExecutionStrategy();
  342. return strategy.ExecuteAsync(() => func(dbContext));
  343. }
  344. }
  345. }