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));
}
}