Skip to content

Commit

Permalink
efcor增加nolock查询扩展
Browse files Browse the repository at this point in the history
  • Loading branch information
ldqk committed Apr 13, 2023
1 parent 3c097dc commit 16adb05
Show file tree
Hide file tree
Showing 10 changed files with 273 additions and 51 deletions.
2 changes: 1 addition & 1 deletion Masuit.Tools.Abstractions/Masuit.Tools.Abstractions.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<TargetFrameworks>netstandard2.0;netstandard2.1;net461;net5;net6;net7</TargetFrameworks>
<LangVersion>latest</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Version>2.5.9.6</Version>
<Version>2.5.9.7</Version>
<Authors>懒得勤快</Authors>
<Description>Masuit.Tools基础公共库,包含一些常用的操作类,大都是静态类,加密解密,反射操作,Excel简单导出,权重随机筛选算法,分布式短id,表达式树,linq扩展,文件压缩,多线程下载和FTP客户端,硬件信息,字符串扩展方法,日期时间扩展操作,中国农历,大文件拷贝,图像裁剪,验证码,断点续传,集合扩展等常用封装。</Description>
<Copyright>懒得勤快,长空X</Copyright>
Expand Down
16 changes: 16 additions & 0 deletions Masuit.Tools.Abstractions/Systems/ConcurrentHashSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,22 @@ public void Add(T item)
}
}

public bool TryAdd(T item)
{
_lock.EnterWriteLock();
try
{
return _hashSet.Add(item);
}
finally
{
if (_lock.IsWriteLockHeld)
{
_lock.ExitWriteLock();
}
}
}

public void UnionWith(IEnumerable<T> other)
{
_lock.EnterWriteLock();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using System.Data.Common;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Diagnostics;

namespace Masuit.Tools.Core;

public class QueryWithNoLockDbCommandInterceptor : DbCommandInterceptor
{
private static readonly Regex TableAliasRegex = new Regex(@"(?<tableAlias>AS \[[a-zA-Z]\w*\](?! WITH \(NOLOCK\)))", RegexOptions.Multiline | RegexOptions.Compiled | RegexOptions.IgnoreCase);

public override InterceptionResult<object> ScalarExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<object> result)
{
command.CommandText = TableAliasRegex.Replace(
command.CommandText,
"${tableAlias} WITH (NOLOCK)"
);
return base.ScalarExecuting(command, eventData, result);
}

#if NETCOREAPP3_1

public override Task<InterceptionResult<object>> ScalarExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<object> result,
CancellationToken cancellationToken = new CancellationToken())
{
command.CommandText = TableAliasRegex.Replace(
command.CommandText,
"${tableAlias} WITH (NOLOCK)"
);
return base.ScalarExecutingAsync(command, eventData, result, cancellationToken);
}

#else
public override ValueTask<InterceptionResult<object>> ScalarExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<object> result,
CancellationToken cancellationToken = new CancellationToken())
{
command.CommandText = TableAliasRegex.Replace(
command.CommandText,
"${tableAlias} WITH (NOLOCK)"
);
return base.ScalarExecutingAsync(command, eventData, result, cancellationToken);
}
#endif

public override InterceptionResult<DbDataReader> ReaderExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result)
{
command.CommandText = TableAliasRegex.Replace(
command.CommandText,
"${tableAlias} WITH (NOLOCK)"
);
return result;
}

#if NETCOREAPP3_1

public override Task<InterceptionResult<DbDataReader>> ReaderExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result,
CancellationToken cancellationToken = new CancellationToken())
{
command.CommandText = TableAliasRegex.Replace(
command.CommandText,
"${tableAlias} WITH (NOLOCK)"
);
return base.ReaderExecutingAsync(command, eventData, result, cancellationToken);
}

#else
public override ValueTask<InterceptionResult<DbDataReader>> ReaderExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result,
CancellationToken cancellationToken = new CancellationToken())
{
command.CommandText = TableAliasRegex.Replace(
command.CommandText,
"${tableAlias} WITH (NOLOCK)"
);
return base.ReaderExecutingAsync(command, eventData, result, cancellationToken);
}
#endif
}
16 changes: 14 additions & 2 deletions Masuit.Tools.AspNetCore/Masuit.Tools.AspNetCore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<Product>Masuit.Tools.AspNetCore</Product>
<PackageId>Masuit.Tools.AspNetCore</PackageId>
<LangVersion>latest</LangVersion>
<Version>1.1.9.6</Version>
<Version>1.1.9.7</Version>
<RepositoryType></RepositoryType>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<FileVersion>1.1.9</FileVersion>
Expand Down Expand Up @@ -45,5 +45,17 @@
<ItemGroup>
<ProjectReference Include="..\Masuit.Tools.Core\Masuit.Tools.Core.csproj" />
</ItemGroup>


<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.1'">
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="3.1.9" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net5'">
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.11" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net6'">
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.10" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net7'">
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.5" />
</ItemGroup>
</Project>
26 changes: 26 additions & 0 deletions Masuit.Tools.Core/Database/ChangeEntry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Microsoft.EntityFrameworkCore;

namespace Masuit.Tools.Core;

public class ChangeEntry
{
/// <summary>
/// 所属实体
/// </summary>
public object Entity { get; set; }

/// <summary>
/// 实体类型
/// </summary>
public Type EntityType { get; set; }

/// <summary>
/// 变更类型
/// </summary>
public EntityState EntityState { get; set; }

/// <summary>
/// 字段变更信息
/// </summary>
public List<ChangePropertyInfo> ChangeProperties { get; set; }
}
31 changes: 31 additions & 0 deletions Masuit.Tools.Core/Database/ChangePropertyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System.Reflection;

namespace Masuit.Tools.Core;

public class ChangePropertyInfo
{
/// <summary>
/// 属性
/// </summary>
public PropertyInfo PropertyInfo { get; set; }

/// <summary>
/// 原始值
/// </summary>
public object OriginalValue { get; set; }

/// <summary>
/// 新值
/// </summary>
public object CurrentValue { get; set; }

/// <summary>
/// 是否是主键
/// </summary>
public bool IsPrimaryKey { get; set; }

/// <summary>
/// 是否是外键
/// </summary>
public bool IsForeignKey { get; set; }
}
149 changes: 104 additions & 45 deletions Masuit.Tools.Core/Database/DbContextExt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Transactions;

namespace Masuit.Tools.Core
{
Expand Down Expand Up @@ -195,8 +195,7 @@ public static IEnumerable<ChangeEntry> GetAllChanges(this DbContext db)
return GetChanges(db).Union(GetAdded(db)).Union(GetRemoved(db));
}

public static IQueryable<TEntity> IncludeRecursive<TEntity>(this IQueryable<TEntity> source,
int levelIndex, Expression<Func<TEntity, ICollection<TEntity>>> expression) where TEntity : class
public static IQueryable<TEntity> IncludeRecursive<TEntity>(this IQueryable<TEntity> source, int levelIndex, Expression<Func<TEntity, ICollection<TEntity>>> expression) where TEntity : class
{
if (levelIndex < 0)
throw new ArgumentOutOfRangeException(nameof(levelIndex));
Expand All @@ -209,58 +208,118 @@ public static IQueryable<TEntity> IncludeRecursive<TEntity>(this IQueryable<TEnt
sb.Append(Type.Delimiter);
sb.Append(property);
}

return source.Include(sb.ToString());
}
}

public class ChangePropertyInfo
{
/// <summary>
/// 属性
/// </summary>
public PropertyInfo PropertyInfo { get; set; }
public static async Task<List<T>> ToListWithNoLockAsync<T>(this IQueryable<T> query, CancellationToken cancellationToken = default)
{
using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions()
{
IsolationLevel = IsolationLevel.ReadUncommitted
}, TransactionScopeAsyncFlowOption.Enabled);
var result = await query.ToListAsync(cancellationToken);
scope.Complete();
return result;
}

/// <summary>
/// 原始值
/// </summary>
public object OriginalValue { get; set; }
public static async Task<int> CountWithNoLockAsync<T>(this IQueryable<T> query, CancellationToken cancellationToken = default)
{
using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions()
{
IsolationLevel = IsolationLevel.ReadUncommitted
}, TransactionScopeAsyncFlowOption.Enabled);
var result = await query.CountAsync(cancellationToken);
scope.Complete();
return result;
}

/// <summary>
/// 新值
/// </summary>
public object CurrentValue { get; set; }
public static async Task<int> CountWithNoLockAsync<T>(this IQueryable<T> query, Expression<Func<T, bool>> where, CancellationToken cancellationToken = default)
{
using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions()
{
IsolationLevel = IsolationLevel.ReadUncommitted
}, TransactionScopeAsyncFlowOption.Enabled);
var result = await query.CountAsync(where, cancellationToken);
scope.Complete();
return result;
}

/// <summary>
/// 是否是主键
/// </summary>
public bool IsPrimaryKey { get; set; }
public static async Task<bool> AnyWithNoLockAsync<T>(this IQueryable<T> query, CancellationToken cancellationToken = default)
{
using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions()
{
IsolationLevel = IsolationLevel.ReadUncommitted
}, TransactionScopeAsyncFlowOption.Enabled);
var result = await query.AnyAsync(cancellationToken);
scope.Complete();
return result;
}

/// <summary>
/// 是否是外键
/// </summary>
public bool IsForeignKey { get; set; }
}
public static async Task<bool> AnyWithNoLockAsync<T>(this IQueryable<T> query, Expression<Func<T, bool>> where, CancellationToken cancellationToken = default)
{
using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions()
{
IsolationLevel = IsolationLevel.ReadUncommitted
}, TransactionScopeAsyncFlowOption.Enabled);
var result = await query.AnyAsync(where, cancellationToken);
scope.Complete();
return result;
}

public class ChangeEntry
{
/// <summary>
/// 所属实体
/// </summary>
public object Entity { get; set; }
public static async Task<bool> AllWithNoLockAsync<T>(this IQueryable<T> query, Expression<Func<T, bool>> where, CancellationToken cancellationToken = default)
{
using var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions()
{
IsolationLevel = IsolationLevel.ReadUncommitted
}, TransactionScopeAsyncFlowOption.Enabled);
var result = await query.AllAsync(where, cancellationToken);
scope.Complete();
return result;
}

/// <summary>
/// 实体类型
/// </summary>
public Type EntityType { get; set; }
public static T NoLock<T, TDbContext>(this TDbContext dbContext, Func<TDbContext, T> 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;
});
}

/// <summary>
/// 变更类型
/// </summary>
public EntityState EntityState { get; set; }
public static Task<T> NoLock<T, TDbContext>(this TDbContext dbContext, Func<TDbContext, Task<T>> 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;
});
}

/// <summary>
/// 字段变更信息
/// </summary>
public List<ChangePropertyInfo> ChangeProperties { get; set; }
public static T ExecutionStrategy<T, TDbContext>(this TDbContext dbContext, Func<TDbContext, T> func) where TDbContext : DbContext
{
var strategy = dbContext.Database.CreateExecutionStrategy();
return strategy.Execute(() => func(dbContext));
}

public static Task<T> ExecutionStrategy<T, TDbContext>(this TDbContext dbContext, Func<TDbContext, Task<T>> func) where TDbContext : DbContext
{
var strategy = dbContext.Database.CreateExecutionStrategy();
return strategy.ExecuteAsync(() => func(dbContext));
}
}
}
2 changes: 1 addition & 1 deletion Masuit.Tools.Core/Masuit.Tools.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
官网教程:https://ldqk.org/55
github:https://github.com/ldqk/Masuit.Tools
</Description>
<Version>2.5.9.6</Version>
<Version>2.5.9.7</Version>
<Copyright>Copyright © 懒得勤快</Copyright>
<PackageProjectUrl>https://github.com/ldqk/Masuit.Tools</PackageProjectUrl>
<PackageTags>Masuit.Tools,工具库,Utility,Crypt,Extensions</PackageTags>
Expand Down
2 changes: 1 addition & 1 deletion Masuit.Tools.Net45/package.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<package>
<metadata>
<id>Masuit.Tools.Net45</id>
<version>2.5.9.6</version>
<version>2.5.9.7</version>
<title>Masuit.Tools</title>
<authors>懒得勤快</authors>
<owners>masuit.com</owners>
Expand Down
Loading

0 comments on commit 16adb05

Please sign in to comment.