I've been trying to port our EF6 project to EF-Core-2.0.
In EF6, we were using DbNolock interceptor for adding With (NOLOCK) hint which queries we want. You can find my ex-running Db interceptor code below.
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) ");
}
}
In Ef-Core, we can make interception nearly same way. But because of changing naming convention of tables, I couldn't write a Regex for the new one. You can find the new Ef-Core version below:
public class DbNoLockListener
{
private static readonly Regex TableAliasRegex = new Regex(@"((?<!\){1,5})AS \[Extent\d+\](?! WITH \(NOLOCK\)))", RegexOptions.Multiline | RegexOptions.IgnoreCase);
[DiagnosticName("Microsoft.EntityFrameworkCore.Database.Command.CommandExecuting")]
public void OnCommandExecuting(DbCommand command, DbCommandMethod executeMethod, Guid commandId, Guid connectionId, bool async, DateTimeOffset startTime)
{
command.CommandText =
TableAliasRegex.Replace(command.CommandText, mt => mt.Groups[0].Value + " WITH (NOLOCK) ");
}
}
Ef6 Generated SQL:
SELECT
[Extent1].[Id] AS [Extent1Id],
[Extent2].[Id] AS [Extent2Id]
FROM [Advert].[Advert] AS [Extent1]
INNER JOIN [Membership].[Members] AS [Extent2] ON [Extent1].[MemberId] = [Extent2].[MemberId]
Ef-Core Genereated 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]
You can also take a look this github issue for more detail.
I want to replace AS [t] with AS [t] WITH (NOLOCK) and AS [t.Member] with AS [t.Member] WITH (NOLOCK)
Which pattern can I use to do the same in Ef-Core?
This interception method doesn't look good to me. A better ways IMO is to hook into EF Core infrastructure to replace the IQuerySqlGenerator service implementation for SqlServer with custom implementation overriding the
VisitTable
method like this:Hooking is a bit complicated because we need to create and replace the "factory" service in order to be able to replace the sql generator. The full code for all that, along with helper extension method is as follows:
Quite a bit code for adding just one meaningful line, but the benefit it that it does it the way EF Core would probably do it in case there is such query option.
Anyway, with the above code all you need is to activate it from your context
OnConfiguring
override:The equivalent of the interceptor can be done by hooking into the
DiagnosticSource
infrastructure.First create an interceptor:
Next, create a global listener for EF diagnostics. Something like:
And register this as part of application startup