DbContextExt.cs 20 KB

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