using Microsoft.EntityFrameworkCore; using System.Collections; using System.Linq.Expressions; using System.Text; using System.Transactions; using Masuit.Tools.Systems; using Masuit.Tools.Reflection; using Microsoft.EntityFrameworkCore.Query.Internal; using Microsoft.EntityFrameworkCore.Query; namespace Masuit.Tools.Core; public static class DbContextExt { /// /// 获取变化的实体信息 /// /// /// /// public static IEnumerable> GetChanges(this DbContext db) { return db.ChangeTracker.Entries().Where(e => e is { State: EntityState.Modified, Entity: T }).Select(e => { NullableDictionary originalObject = e.OriginalValues.ToObject() as Dictionary ?? new NullableDictionary(); NullableDictionary currentObject = e.CurrentValues.ToObject() as Dictionary ?? new NullableDictionary(); return new ChangeEntry { EntityState = e.State, Entity = (T)e.Entity, EntityType = e.OriginalValues.EntityType.ClrType, 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 { PropertyInfo = t1.Property.PropertyInfo, OriginalValue = t1.Value, CurrentValue = t2.Value, IsPrimaryKey = t1.Property.IsPrimaryKey(), IsForeignKey = t1.Property.IsForeignKey() }).Where(t => Comparer.Default.Compare(t.OriginalValue, t.CurrentValue) != 0).ToList() }; }); } /// /// 获取变化的实体信息 /// /// /// public static IEnumerable GetChanges(this DbContext db) { return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Modified).Select(e => { NullableDictionary originalObject = e.OriginalValues.ToObject() as Dictionary ?? new NullableDictionary(); NullableDictionary currentObject = e.CurrentValues.ToObject() as Dictionary ?? new NullableDictionary(); return new ChangeEntry { EntityState = e.State, Entity = e.Entity, EntityType = e.OriginalValues.EntityType.ClrType, 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 { PropertyInfo = t1.Property.PropertyInfo, OriginalValue = t1.Value, CurrentValue = t2.Value, IsPrimaryKey = t1.Property.IsPrimaryKey(), IsForeignKey = t1.Property.IsForeignKey() }).Where(t => Comparer.Default.Compare(t.OriginalValue, t.CurrentValue) != 0).ToList() }; }); } /// /// 获取添加的实体信息 /// /// /// /// public static IEnumerable> GetAdded(this DbContext db) { return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Added && e.Entity is T).Select(e => { NullableDictionary currentObject = e.CurrentValues.ToObject() as Dictionary ?? new NullableDictionary(); return new ChangeEntry { EntityState = e.State, Entity = (T)e.Entity, EntityType = e.CurrentValues.EntityType.ClrType, ChangeProperties = e.CurrentValues.Properties.Select(p => new ChangePropertyInfo() { PropertyInfo = p.PropertyInfo, CurrentValue = currentObject[p.PropertyInfo?.Name], IsPrimaryKey = p.IsPrimaryKey(), IsForeignKey = p.IsForeignKey(), }).ToList() }; }); } /// /// 获取添加的实体信息 /// /// /// public static IEnumerable GetAdded(this DbContext db) { return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Added).Select(e => { NullableDictionary currentObject = e.CurrentValues.ToObject() as Dictionary ?? new NullableDictionary(); return new ChangeEntry { EntityState = e.State, Entity = e.Entity, EntityType = e.CurrentValues.EntityType.ClrType, ChangeProperties = e.CurrentValues.Properties.Select(p => new ChangePropertyInfo { PropertyInfo = p.PropertyInfo, CurrentValue = currentObject[p.PropertyInfo?.Name], IsPrimaryKey = p.IsPrimaryKey(), IsForeignKey = p.IsForeignKey(), }).ToList() }; }); } /// /// 获取移除的实体信息 /// /// /// /// public static IEnumerable> GetRemoved(this DbContext db) { return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Deleted && e.Entity is T).Select(e => { NullableDictionary originalObject = e.OriginalValues.ToObject() as Dictionary ?? new NullableDictionary(); return new ChangeEntry { EntityState = e.State, Entity = (T)e.Entity, EntityType = e.OriginalValues.EntityType.ClrType, ChangeProperties = e.OriginalValues.Properties.Select(p => new ChangePropertyInfo { PropertyInfo = p.PropertyInfo, OriginalValue = originalObject[p.PropertyInfo?.Name], IsPrimaryKey = p.IsPrimaryKey(), IsForeignKey = p.IsForeignKey(), }).ToList() }; }); } /// /// 获取移除的实体信息 /// /// /// public static IEnumerable GetRemoved(this DbContext db) { return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Deleted).Select(e => { NullableDictionary originalObject = e.OriginalValues.ToObject() as Dictionary ?? new NullableDictionary(); return new ChangeEntry { EntityState = e.State, Entity = e.Entity, EntityType = e.OriginalValues.EntityType.ClrType, ChangeProperties = e.OriginalValues.Properties.Select(p => new ChangePropertyInfo { PropertyInfo = p.PropertyInfo, OriginalValue = originalObject[p.PropertyInfo?.Name], IsPrimaryKey = p.IsPrimaryKey(), IsForeignKey = p.IsForeignKey(), }).ToList() }; }); } /// /// 获取所有的变更信息 /// /// /// /// public static IEnumerable> GetAllChanges(this DbContext db) { return GetChanges(db).Union(GetAdded(db)).Union(GetRemoved(db)); } /// /// 获取所有的变更信息 /// /// /// public static IEnumerable GetAllChanges(this DbContext db) { return GetChanges(db).Union(GetAdded(db)).Union(GetRemoved(db)); } public static IQueryable IncludeRecursive(this IQueryable source, int levelIndex, Expression>> expression) where TEntity : class { if (levelIndex < 0) throw new ArgumentOutOfRangeException(nameof(levelIndex)); var member = (MemberExpression)expression.Body; var property = member.Member.Name; var sb = new StringBuilder(); for (int i = 0; i < levelIndex; i++) { if (i > 0) sb.Append(Type.Delimiter); sb.Append(property); } return source.Include(sb.ToString()); } public static Task> ToListWithNoLockAsync(this IQueryable query, CancellationToken cancellationToken = default) { return ExecuteStrategyAsync(query, async (q) => { using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted }, TransactionScopeAsyncFlowOption.Enabled); var result = await q.ToListAsync(cancellationToken); scope.Complete(); return result; }); } public static List ToListWithNoLock(this IQueryable query) { return ExecuteStrategy(query, q => { using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted }, TransactionScopeAsyncFlowOption.Enabled); var result = q.ToList(); scope.Complete(); return result; }); } public static Task CountWithNoLockAsync(this IQueryable query, CancellationToken cancellationToken = default) { return ExecuteStrategyAsync(query, async q => { using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted }, TransactionScopeAsyncFlowOption.Enabled); var result = await q.CountAsync(cancellationToken); scope.Complete(); return result; }); } public static int CountWithNoLock(this IQueryable query) { return ExecuteStrategy(query, q => { using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted }, TransactionScopeAsyncFlowOption.Enabled); var result = q.Count(); scope.Complete(); return result; }); } public static Task CountWithNoLockAsync(this IQueryable query, Expression> where, CancellationToken cancellationToken = default) { return ExecuteStrategyAsync(query, async q => { using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted }, TransactionScopeAsyncFlowOption.Enabled); var result = await q.CountAsync(where, cancellationToken); scope.Complete(); return result; }); } public static int CountWithNoLock(this IQueryable query, Expression> where) { return ExecuteStrategy(query, q => { using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted }, TransactionScopeAsyncFlowOption.Enabled); var result = q.Count(where); scope.Complete(); return result; }); } public static Task AnyWithNoLockAsync(this IQueryable query, CancellationToken cancellationToken = default) { return ExecuteStrategyAsync(query, async q => { using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted }, TransactionScopeAsyncFlowOption.Enabled); var result = await q.AnyAsync(cancellationToken); scope.Complete(); return result; }); } public static bool AnyWithNoLock(this IQueryable query) { return ExecuteStrategy(query, q => { using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted }, TransactionScopeAsyncFlowOption.Enabled); var result = q.Any(); scope.Complete(); return result; }); } public static Task AnyWithNoLockAsync(this IQueryable query, Expression> where, CancellationToken cancellationToken = default) { return ExecuteStrategyAsync(query, async q => { using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted }, TransactionScopeAsyncFlowOption.Enabled); var result = await q.AnyAsync(where, cancellationToken); scope.Complete(); return result; }); } public static bool AnyWithNoLock(this IQueryable query, Expression> where) { return ExecuteStrategy(query, q => { using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted }, TransactionScopeAsyncFlowOption.Enabled); var result = q.Any(where); scope.Complete(); return result; }); } public static Task FirstOrDefaultWithNoLockAsync(this IQueryable query, Expression> where, CancellationToken cancellationToken = default) { return ExecuteStrategyAsync(query, async q => { using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted }, TransactionScopeAsyncFlowOption.Enabled); var result = await q.FirstOrDefaultAsync(where, cancellationToken); scope.Complete(); return result; }); } public static T FirstOrDefaultWithNoLock(this IQueryable query, Expression> where) { return ExecuteStrategy(query, q => { using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted }, TransactionScopeAsyncFlowOption.Enabled); var result = q.FirstOrDefault(where); scope.Complete(); return result; }); } public static Task FirstOrDefaultWithNoLockAsync(this IQueryable query, CancellationToken cancellationToken = default) { return ExecuteStrategyAsync(query, async q => { using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadUncommitted }, TransactionScopeAsyncFlowOption.Enabled); var result = await q.FirstOrDefaultAsync(cancellationToken); scope.Complete(); return result; }); } public static T FirstOrDefaultWithNoLock(this IQueryable query) { return ExecuteStrategy(query, q => { using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadUncommitted }, TransactionScopeAsyncFlowOption.Enabled); var result = q.FirstOrDefault(); scope.Complete(); return result; }); } public static Task SingleOrDefaultWithNoLockAsync(this IQueryable query, Expression> where, CancellationToken cancellationToken = default) { return ExecuteStrategyAsync(query, async q => { using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadUncommitted }, TransactionScopeAsyncFlowOption.Enabled); var result = await q.SingleOrDefaultAsync(where, cancellationToken); scope.Complete(); return result; }); } public static T SingleOrDefaultWithNoLock(this IQueryable query, Expression> where) { return ExecuteStrategy(query, q => { using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted }, TransactionScopeAsyncFlowOption.Enabled); var result = q.SingleOrDefault(where); scope.Complete(); return result; }); } public static Task SingleOrDefaultWithNoLockAsync(this IQueryable query, CancellationToken cancellationToken = default) { return ExecuteStrategyAsync(query, async q => { using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted }, TransactionScopeAsyncFlowOption.Enabled); var result = await q.SingleOrDefaultAsync(cancellationToken); scope.Complete(); return result; }); } public static T SingleOrDefaultWithNoLock(this IQueryable query) { return ExecuteStrategy(query, q => { using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted }, TransactionScopeAsyncFlowOption.Enabled); var result = q.SingleOrDefault(); scope.Complete(); return result; }); } public static Task AllWithNoLockAsync(this IQueryable query, Expression> where, CancellationToken cancellationToken = default) { return ExecuteStrategyAsync(query, async q => { using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted }, TransactionScopeAsyncFlowOption.Enabled); var result = await q.AllAsync(where, cancellationToken); scope.Complete(); return result; }); } public static bool AllWithNoLock(this IQueryable query, Expression> where) { return ExecuteStrategy(query, q => { using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted }, TransactionScopeAsyncFlowOption.Enabled); var result = query.All(where); scope.Complete(); return result; }); } public static T NoLock(this TDbContext dbContext, Func func) where TDbContext : DbContext { var strategy = dbContext.Database.CreateExecutionStrategy(); return strategy.Execute(() => { var transactionOptions = new TransactionOptions { IsolationLevel = IsolationLevel.ReadUncommitted }; using var scope = new TransactionScope(TransactionScopeOption.Required, transactionOptions); var result = func(dbContext); scope.Complete(); return result; }); } public static Task NoLock(this TDbContext dbContext, Func> func) where TDbContext : DbContext { var strategy = dbContext.Database.CreateExecutionStrategy(); return strategy.ExecuteAsync(async () => { var transactionOptions = new TransactionOptions { IsolationLevel = IsolationLevel.ReadUncommitted }; using var scope = new TransactionScope(TransactionScopeOption.Required, transactionOptions); var result = await func(dbContext); scope.Complete(); return result; }); } public static T ExecuteStrategy(this TDbContext dbContext, Func func) where TDbContext : DbContext { var strategy = dbContext.Database.CreateExecutionStrategy(); return strategy.Execute(() => func(dbContext)); } public static Task ExecuteStrategy(this TDbContext dbContext, Func> func) where TDbContext : DbContext { var strategy = dbContext.Database.CreateExecutionStrategy(); return strategy.ExecuteAsync(() => func(dbContext)); } public static TResult ExecuteStrategy(this IQueryable query, Func, TResult> func) { if (query.Provider is not EntityQueryProvider) { return func(query); } var dependencies = query.Provider.GetField("_queryCompiler").GetField("_compiledQueryCacheKeyGenerator").GetField("Dependencies"); #if NETSTANDARD2_0 var context = dependencies.Context.Context; #else var context = dependencies.CurrentContext.Context; #endif var strategy = context.Database.CreateExecutionStrategy(); return strategy.Execute(() => func(query)); } public static Task ExecuteStrategyAsync(this IQueryable query, Func, Task> func) { if (query.Provider is not EntityQueryProvider) { return func(query); } var dependencies = query.Provider.GetField("_queryCompiler").GetField("_compiledQueryCacheKeyGenerator").GetField("Dependencies"); #if NETSTANDARD2_0 var context = dependencies.Context.Context; #else var context = dependencies.CurrentContext.Context; #endif var strategy = context.Database.CreateExecutionStrategy(); return strategy.ExecuteAsync(() => func(query)); } }