C# Ef Core-在Db拦截器中,我可以使用什么正则表达式将表名替换为nolock表名
我一直在尝试将我们的EF6项目移植到EF-Core-2.0 在EF6中,我们使用DbNolock拦截器添加和(NOLOCK)提示我们需要的查询。你可以在下面找到我的ex running Db interceptor代码C# Ef Core-在Db拦截器中,我可以使用什么正则表达式将表名替换为nolock表名,c#,regex,entity-framework,.net-core,entity-framework-core,C#,Regex,Entity Framework,.net Core,Entity Framework Core,我一直在尝试将我们的EF6项目移植到EF-Core-2.0 在EF6中,我们使用DbNolock拦截器添加和(NOLOCK)提示我们需要的查询。你可以在下面找到我的ex running Db interceptor代码 public class DbNoLockInterceptor : DbCommandInterceptor { private static readonly Regex TableAliasRegex = new Regex(@"((?<!\){
public class DbNoLockInterceptor : DbCommandInterceptor
{
private static readonly Regex TableAliasRegex = new Regex(@"((?<!\){1,5})AS \[Extent\d+\](?! WITH \(NOLOCK\)))", RegexOptions.Multiline | RegexOptions.IgnoreCase);
public override void ScalarExecuting(DbCommand command,
DbCommandInterceptionContext<object> interceptionContext)
{
command.CommandText =
TableAliasRegex.Replace(command.CommandText, mt => mt.Groups[0].Value + " WITH (NOLOCK) ");
}
public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
command.CommandText = TableAliasRegex.Replace(command.CommandText, mt => mt.Groups[0].Value + " WITH (NOLOCK) ");
}
}
Ef核心通用SQL:
SELECT
[t].[Id]
,[t.Member].[Id]
FROM [Advert].[Advert] AS [t]
INNER JOIN [Membership].[Members] AS [t.Member] ON [t].[MemberId] = [t.Member].[MemberId]
你也可以看看
我想换一个
AS[t]与AS[t]与(NOLOCK)和
作为[t.Member]与作为[t.Member]与(NOLOCK)
我可以使用哪种模式在Ef Core中执行相同的操作?通过连接到
DiagnosticsSource
基础设施,可以完成与拦截器等效的操作
首先创建一个拦截器:
public class NoLockInterceptor : IObserver<KeyValuePair<string, object>>
{
public void OnCompleted()
{
}
public void OnError(Exception error)
{
}
public void OnNext(KeyValuePair<string, object> value)
{
if (value.Key == RelationalEventId.CommandExecuting.Name)
{
var command = ((CommandEventData)value.Value).Command;
// Do command.CommandText manipulation here
}
}
}
这种拦截方法在我看来不太好。IMO的一个更好的方法是连接到EF核心基础设施,用覆盖
VisitTable
方法的自定义实现替换SqlServer的服务实现,如下所示:
public override Expression VisitTable(TableExpression tableExpression)
{
// base will append schema, table and alias
var result = base.VisitTable(tableExpression);
Sql.Append(" WITH (NOLOCK)");
return result;
}
挂钩有点复杂,因为我们需要创建和替换“工厂”服务,以便能够替换sql生成器。所有这些的完整代码以及辅助扩展方法如下所示:
EF Core 3.x:
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.SqlServer.Query.Sql.Internal;
using Microsoft.EntityFrameworkCore.SqlServer.Query.Internal;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
namespace Microsoft.EntityFrameworkCore
{
public static class CustomDbContextOptionsBuilderExtensions
{
public static DbContextOptionsBuilder UseCustomSqlServerQuerySqlGenerator(this DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.ReplaceService<IQuerySqlGeneratorFactory, CustomSqlServerQuerySqlGeneratorFactory>();
return optionsBuilder;
}
}
}
namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Sql.Internal
{
class CustomSqlServerQuerySqlGeneratorFactory : IQuerySqlGeneratorFactory
{
public CustomSqlServerQuerySqlGeneratorFactory(QuerySqlGeneratorDependencies dependencies)
=> Dependencies = dependencies;
public QuerySqlGeneratorDependencies Dependencies { get; }
public QuerySqlGenerator Create() => new CustomSqlServerQuerySqlGenerator(Dependencies);
}
public class CustomSqlServerQuerySqlGenerator : SqlServerQuerySqlGenerator
{
public CustomSqlServerQuerySqlGenerator(QuerySqlGeneratorDependencies dependencies)
: base(dependencies) { }
protected override Expression VisitTable(TableExpression tableExpression)
{
// base will append schema, table and alias
var result = base.VisitTable(tableExpression);
Sql.Append(" WITH (NOLOCK)");
return result;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore.Query.Expressions;
using Microsoft.EntityFrameworkCore.Query.Sql;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal;
using Microsoft.EntityFrameworkCore.SqlServer.Query.Sql.Internal;
namespace Microsoft.EntityFrameworkCore
{
public static class CustomDbContextOptionsBuilderExtensions
{
public static DbContextOptionsBuilder UseCustomSqlServerQuerySqlGenerator(this DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.ReplaceService<IQuerySqlGeneratorFactory, CustomSqlServerQuerySqlGeneratorFactory>();
return optionsBuilder;
}
}
}
namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Sql.Internal
{
class CustomSqlServerQuerySqlGeneratorFactory : SqlServerQuerySqlGeneratorFactory
{
private readonly ISqlServerOptions sqlServerOptions;
public CustomSqlServerQuerySqlGeneratorFactory(QuerySqlGeneratorDependencies dependencies, ISqlServerOptions sqlServerOptions)
: base(dependencies, sqlServerOptions) => this.sqlServerOptions = sqlServerOptions;
public override IQuerySqlGenerator CreateDefault(SelectExpression selectExpression) =>
new CustomSqlServerQuerySqlGenerator(Dependencies, selectExpression, sqlServerOptions.RowNumberPagingEnabled);
}
public class CustomSqlServerQuerySqlGenerator : SqlServerQuerySqlGenerator
{
public CustomSqlServerQuerySqlGenerator(QuerySqlGeneratorDependencies dependencies, SelectExpression selectExpression, bool rowNumberPagingEnabled)
: base(dependencies, selectExpression, rowNumberPagingEnabled) { }
public override Expression VisitTable(TableExpression tableExpression)
{
// base will append schema, table and alias
var result = base.VisitTable(tableExpression);
Sql.Append(" WITH (NOLOCK)");
return result;
}
}
}
你到底为什么还在用efcore命令?如果你是的话,为什么还要麻烦升级呢?我只是不明白这有什么意义。@JohnB你能解释一下你的意思吗,因为我不清楚。以上功能不是现成提供的。。。所以我不明白你的意思。这里的正则表达式问题是什么?您希望匹配哪个字符串,不应该匹配哪个字符串,您希望得到什么结果?注意:如果您使用
“$”WITH(NOLOCK)”
而不是mt=>mt.Groups[0],则可以大大简化替换过程。值+“WITH(NOLOCK)”
可以通过连接到DiagnosticSource InfrastructureRanks来实现拦截器的等效功能,John。我已经用不同的方式完成了这部分。我的确切问题是://Do command.CommandText manipulation这里我如何使用nolock操作sql?我想说的是,上述方法可能会降低长期维护的成本,因为您不必处理脆弱的正则表达式code@JohnB好的,如果没有脆弱的正则表达式代码,我们怎么做呢?您的代码没有包含解决方案。我错过了什么吗?嗨,@WiktorStribiżew为什么还不清楚?我只想将所有AS[t]**值更改为**AS[t],其中(NOLOCK)没有任何EF Core经验,一般来说,您应该使用自己的命名空间,而不是Microsoft的命名空间。@TsahiAsher为什么不在扩展它们时使用Microsoft或System命名空间?尤其是使用EF Core,所有数据库提供程序都使用自定义扩展方法扩展Microsoft.EntityFrameworkCore
命名空间,如UseSqlServer
、UseMySql
和许多其他扩展方法。整个EF核心fluent API体系结构基于自定义扩展方法。想法是让使用Microsoft.EntityFrameworkCore代码>在你的代码中足以访问所有的代码。好吧,这是一个从中转移出来的。嗨@IvanStoev,这正是我想要的。我已经做了一些测试,它们工作得很好!谢谢你。@IvanStoev非常感谢你为我指明了正确的方向。我在网上的任何地方都找不到这些信息。
DiagnosticListener.AllListeners.Subscribe(new EfGlobalListener());
public override Expression VisitTable(TableExpression tableExpression)
{
// base will append schema, table and alias
var result = base.VisitTable(tableExpression);
Sql.Append(" WITH (NOLOCK)");
return result;
}
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.SqlServer.Query.Sql.Internal;
using Microsoft.EntityFrameworkCore.SqlServer.Query.Internal;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
namespace Microsoft.EntityFrameworkCore
{
public static class CustomDbContextOptionsBuilderExtensions
{
public static DbContextOptionsBuilder UseCustomSqlServerQuerySqlGenerator(this DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.ReplaceService<IQuerySqlGeneratorFactory, CustomSqlServerQuerySqlGeneratorFactory>();
return optionsBuilder;
}
}
}
namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Sql.Internal
{
class CustomSqlServerQuerySqlGeneratorFactory : IQuerySqlGeneratorFactory
{
public CustomSqlServerQuerySqlGeneratorFactory(QuerySqlGeneratorDependencies dependencies)
=> Dependencies = dependencies;
public QuerySqlGeneratorDependencies Dependencies { get; }
public QuerySqlGenerator Create() => new CustomSqlServerQuerySqlGenerator(Dependencies);
}
public class CustomSqlServerQuerySqlGenerator : SqlServerQuerySqlGenerator
{
public CustomSqlServerQuerySqlGenerator(QuerySqlGeneratorDependencies dependencies)
: base(dependencies) { }
protected override Expression VisitTable(TableExpression tableExpression)
{
// base will append schema, table and alias
var result = base.VisitTable(tableExpression);
Sql.Append(" WITH (NOLOCK)");
return result;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore.Query.Expressions;
using Microsoft.EntityFrameworkCore.Query.Sql;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal;
using Microsoft.EntityFrameworkCore.SqlServer.Query.Sql.Internal;
namespace Microsoft.EntityFrameworkCore
{
public static class CustomDbContextOptionsBuilderExtensions
{
public static DbContextOptionsBuilder UseCustomSqlServerQuerySqlGenerator(this DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.ReplaceService<IQuerySqlGeneratorFactory, CustomSqlServerQuerySqlGeneratorFactory>();
return optionsBuilder;
}
}
}
namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Sql.Internal
{
class CustomSqlServerQuerySqlGeneratorFactory : SqlServerQuerySqlGeneratorFactory
{
private readonly ISqlServerOptions sqlServerOptions;
public CustomSqlServerQuerySqlGeneratorFactory(QuerySqlGeneratorDependencies dependencies, ISqlServerOptions sqlServerOptions)
: base(dependencies, sqlServerOptions) => this.sqlServerOptions = sqlServerOptions;
public override IQuerySqlGenerator CreateDefault(SelectExpression selectExpression) =>
new CustomSqlServerQuerySqlGenerator(Dependencies, selectExpression, sqlServerOptions.RowNumberPagingEnabled);
}
public class CustomSqlServerQuerySqlGenerator : SqlServerQuerySqlGenerator
{
public CustomSqlServerQuerySqlGenerator(QuerySqlGeneratorDependencies dependencies, SelectExpression selectExpression, bool rowNumberPagingEnabled)
: base(dependencies, selectExpression, rowNumberPagingEnabled) { }
public override Expression VisitTable(TableExpression tableExpression)
{
// base will append schema, table and alias
var result = base.VisitTable(tableExpression);
Sql.Append(" WITH (NOLOCK)");
return result;
}
}
}
optionsBuilder.UseCustomSqlServerQuerySqlGenerator();