DbContextExt.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. using Microsoft.EntityFrameworkCore;
  2. using System.Collections;
  3. using System.Linq.Expressions;
  4. using System.Text;
  5. using System.Transactions;
  6. namespace Masuit.Tools.Core
  7. {
  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.State == EntityState.Modified && e.Entity is T).Select(e =>
  19. {
  20. var originalObject = e.OriginalValues.ToObject();
  21. var currentObject = e.CurrentValues.ToObject();
  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: p.PropertyInfo?.GetValue(originalObject))).Zip(e.CurrentValues.Properties.Select(p => (Property: p, Value: p.PropertyInfo?.GetValue(currentObject))), (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. var originalObject = e.OriginalValues.ToObject();
  48. var currentObject = e.CurrentValues.ToObject();
  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: p.PropertyInfo?.GetValue(originalObject))).Zip(e.CurrentValues.Properties.Select(p => (Property: p, Value: p.PropertyInfo?.GetValue(currentObject))), (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. var currentObject = e.CurrentValues.ToObject();
  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 = p.PropertyInfo?.GetValue(currentObject),
  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. var currentObject = e.CurrentValues.ToObject();
  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 = p.PropertyInfo?.GetValue(currentObject),
  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. var originalObject = e.OriginalValues.ToObject();
  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 = p.PropertyInfo?.GetValue(originalObject),
  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. var originalObject = e.OriginalValues.ToObject();
  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 = p.PropertyInfo?.GetValue(originalObject),
  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 async Task<int> CountWithNoLockAsync<T>(this IQueryable<T> query, CancellationToken cancellationToken = default)
  212. {
  213. using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions()
  214. {
  215. IsolationLevel = IsolationLevel.ReadUncommitted
  216. }, TransactionScopeAsyncFlowOption.Enabled);
  217. var result = await query.CountAsync(cancellationToken);
  218. scope.Complete();
  219. return result;
  220. }
  221. public static async Task<int> CountWithNoLockAsync<T>(this IQueryable<T> query, Expression<Func<T, bool>> where, 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(where, cancellationToken);
  228. scope.Complete();
  229. return result;
  230. }
  231. public static async Task<bool> AnyWithNoLockAsync<T>(this IQueryable<T> query, CancellationToken cancellationToken = default)
  232. {
  233. using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions()
  234. {
  235. IsolationLevel = IsolationLevel.ReadUncommitted
  236. }, TransactionScopeAsyncFlowOption.Enabled);
  237. var result = await query.AnyAsync(cancellationToken);
  238. scope.Complete();
  239. return result;
  240. }
  241. public static async Task<bool> AnyWithNoLockAsync<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.AnyAsync(where, cancellationToken);
  248. scope.Complete();
  249. return result;
  250. }
  251. public static async Task<T> FirstOrDefaultWithNoLockAsync<T>(this IQueryable<T> query, Expression<Func<T, bool>> where, CancellationToken cancellationToken = default)
  252. {
  253. using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions()
  254. {
  255. IsolationLevel = IsolationLevel.ReadUncommitted
  256. }, TransactionScopeAsyncFlowOption.Enabled);
  257. var result = await query.FirstOrDefaultAsync(where, cancellationToken);
  258. scope.Complete();
  259. return result;
  260. }
  261. public static async Task<T> FirstOrDefaultWithNoLockAsync<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.FirstOrDefaultAsync(cancellationToken);
  268. scope.Complete();
  269. return result;
  270. }
  271. public static async Task<T> SingleOrDefaultWithNoLockAsync<T>(this IQueryable<T> query, Expression<Func<T, bool>> where, CancellationToken cancellationToken = default)
  272. {
  273. using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions()
  274. {
  275. IsolationLevel = IsolationLevel.ReadUncommitted
  276. }, TransactionScopeAsyncFlowOption.Enabled);
  277. var result = await query.SingleOrDefaultAsync(where, cancellationToken);
  278. scope.Complete();
  279. return result;
  280. }
  281. public static async Task<T> SingleOrDefaultWithNoLockAsync<T>(this IQueryable<T> query, 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.SingleOrDefaultAsync(cancellationToken);
  288. scope.Complete();
  289. return result;
  290. }
  291. public static async Task<bool> AllWithNoLockAsync<T>(this IQueryable<T> query, Expression<Func<T, bool>> where, CancellationToken cancellationToken = default)
  292. {
  293. using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions()
  294. {
  295. IsolationLevel = IsolationLevel.ReadUncommitted
  296. }, TransactionScopeAsyncFlowOption.Enabled);
  297. var result = await query.AllAsync(where, cancellationToken);
  298. scope.Complete();
  299. return result;
  300. }
  301. public static T NoLock<T, TDbContext>(this TDbContext dbContext, Func<TDbContext, T> func) where TDbContext : DbContext
  302. {
  303. var strategy = dbContext.Database.CreateExecutionStrategy();
  304. return strategy.Execute(() =>
  305. {
  306. var transactionOptions = new TransactionOptions
  307. {
  308. IsolationLevel = IsolationLevel.ReadUncommitted
  309. };
  310. using var scope = new TransactionScope(TransactionScopeOption.Required, transactionOptions);
  311. var result = func(dbContext);
  312. scope.Complete();
  313. return result;
  314. });
  315. }
  316. public static Task<T> NoLock<T, TDbContext>(this TDbContext dbContext, Func<TDbContext, Task<T>> func) where TDbContext : DbContext
  317. {
  318. var strategy = dbContext.Database.CreateExecutionStrategy();
  319. return strategy.ExecuteAsync(async () =>
  320. {
  321. var transactionOptions = new TransactionOptions
  322. {
  323. IsolationLevel = IsolationLevel.ReadUncommitted
  324. };
  325. using var scope = new TransactionScope(TransactionScopeOption.Required, transactionOptions);
  326. var result = await func(dbContext);
  327. scope.Complete();
  328. return result;
  329. });
  330. }
  331. public static T ExecutionStrategy<T, TDbContext>(this TDbContext dbContext, Func<TDbContext, T> func) where TDbContext : DbContext
  332. {
  333. var strategy = dbContext.Database.CreateExecutionStrategy();
  334. return strategy.Execute(() => func(dbContext));
  335. }
  336. public static Task<T> ExecutionStrategy<T, TDbContext>(this TDbContext dbContext, Func<TDbContext, Task<T>> func) where TDbContext : DbContext
  337. {
  338. var strategy = dbContext.Database.CreateExecutionStrategy();
  339. return strategy.ExecuteAsync(() => func(dbContext));
  340. }
  341. }
  342. }