select * from table1 with(index=IX_table1_1)
使用ado.net实体的Linq to sql想要编写上面的代码。我特别找不到实体,使用索引。
var querysample = from a in db.table1
select a;
答案 0 :(得分:10)
解决方案很简单。 让我们添加一个拦截器!!!
public class HintInterceptor : DbCommandInterceptor
{
private static readonly Regex _tableAliasRegex = new Regex(@"(?<tableAlias>AS \[Extent\d+\](?! WITH \(*HINT*\)))", RegexOptions.Multiline | RegexOptions.IgnoreCase);
[ThreadStatic] public static string HintValue;
public override void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
if (!String.IsNullOrWhiteSpace(HintValue))
{
command.CommandText = _tableAliasRegex.Replace(command.CommandText, "${tableAlias} WITH (*HINT*)");
command.CommandText = command.CommandText.Replace("*HINT*", HintValue);
}
HintValue = String.Empty;
}
public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
if (!String.IsNullOrWhiteSpace(HintValue))
{
command.CommandText = _tableAliasRegex.Replace(command.CommandText, "${tableAlias} WITH (*HINT*)");
command.CommandText = command.CommandText.Replace("*HINT*", HintValue);
}
HintValue = String.Empty;
}
}
我知道正则表达式会更好。 让我们在Config类中注册我们的Interceptor
public class PbsContextConfig : DbConfiguration
{
public PbsContextConfig()
{
this.AddInterceptor(new HintInterceptor());
}
}
让我们为DbSet做出很好的提示扩展
public static class HintExtension
{
public static DbSet<T> WithHint<T>(this DbSet<T> set, string hint) where T : class
{
HintInterceptor.HintValue = hint;
return set;
}
}
如何使用?
context.Persons.WithHint("INDEX(XI_DOWNTIME_LOCK)").Where( x => x.ID == ....
欢迎修改!
答案 1 :(得分:3)
L2S和EF都不会像定制那样提供对SQL的直接支持(索引提示等),尽管使用L2S可以通过ExecuteQuery<T>(...)
(它采用原始TSQL)来实现。如果您需要该级别的控制,请考虑存储过程或备用ORM。
这里特别存在的一个问题是查询提示是特定于平台的,但EF试图与平台无关。
答案 2 :(得分:2)
添加到 stop-crans 答案中,这适用于(我在)EF Core 5.0 并支持 WITH(INDEX(param)) 的参数。
public class QueryHintInterceptor : DbCommandInterceptor
{
private static readonly Regex _tableAliasRegex = new Regex(@"(FROM[\s\r\n]+\S+(?:[\s\r\n]+AS[\s\r\n]+[^\s\r\n]+)?)", RegexOptions.Multiline | RegexOptions.IgnoreCase);
private readonly string _hintPrefix;
public QueryHintInterceptor(string hintPrefix)
{
_hintPrefix = "-- " + hintPrefix;
}
public override InterceptionResult<DbDataReader> ReaderExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result)
{
PatchCommandtext(command);
return base.ReaderExecuting(command, eventData, result);
}
public override ValueTask<InterceptionResult<DbDataReader>> ReaderExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result, CancellationToken cancellationToken = default)
{
PatchCommandtext(command);
return base.ReaderExecutingAsync(command, eventData, result, cancellationToken);
}
public override InterceptionResult<object> ScalarExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<object> result)
{
PatchCommandtext(command);
return base.ScalarExecuting(command, eventData, result);
}
public override ValueTask<InterceptionResult<object>> ScalarExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<object> result, CancellationToken cancellationToken = default)
{
PatchCommandtext(command);
return base.ScalarExecutingAsync(command, eventData, result, cancellationToken);
}
private void PatchCommandtext(DbCommand command)
{
if (command.CommandText.StartsWith(_hintPrefix, StringComparison.Ordinal))
{
int index = command.CommandText.IndexOfAny(Environment.NewLine.ToCharArray(), _hintPrefix.Length);
command.CommandText = _tableAliasRegex
.Replace(command.CommandText, "${0} WITH (" + command.CommandText
.Substring(_hintPrefix.Length, index - _hintPrefix.Length) + ")")
.Substring(index);
}
}
}
public static class QueryHintsDbContextOptionsBuilderExtensions
{
private const string HintTag = "Use hint: ";
public static IQueryable<T> WithHint<T>(this IQueryable<T> source, TableHint hint) =>
source.TagWith(HintTag + hint);
public static IQueryable<T> WithHint<T>(this IQueryable<T> source, TableHint hint, string param) =>
source.TagWith(HintTag + hint + " (" + param+")");
public static DbContextOptionsBuilder AddQueryHints(this DbContextOptionsBuilder builder) =>
builder.AddInterceptors(new QueryHintInterceptor(HintTag));
}
public enum TableHint
{
Index,
KeepIdentity,
KeepDefaults,
HoldLock,
Ignore_Constraints,
Ignore_Triggers,
Nolock,
NoWait,
PagLock,
ReadCommitted,
ReadCommittedLock,
ReadPast,
RepeatableRead,
RowLock,
Serializable,
Snapshot,
TabLock,
TabLockX,
UpdLock,
Xlock
}
答案 3 :(得分:1)
除了 wh1sp3r 的答案,请参阅下面的另一个拦截器,它依赖于 EF query tags 而不是线程静态变量:
Visitor
扩展方法:
public class QueryHintInterceptor : DbCommandInterceptor
{
private static readonly Regex _tableAliasRegex = new Regex("( AS [^ ]+)",
RegexOptions.Multiline | RegexOptions.IgnoreCase);
private readonly string _hintPrefix;
public QueryHintInterceptor(string hintPrefix)
{
_hintPrefix = "-- " + hintPrefix;
}
public override InterceptionResult<DbDataReader> ReaderExecuting(DbCommand command,
CommandEventData eventData, InterceptionResult<DbDataReader> result)
{
PatchCommandText(command);
return base.ReaderExecuting(command, eventData, result);
}
public override ValueTask<InterceptionResult<DbDataReader>> ReaderExecutingAsync(
DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result,
CancellationToken cancellationToken = default)
{
PatchCommandText(command);
return base.ReaderExecutingAsync(command, eventData, result, cancellationToken);
}
public override InterceptionResult<object> ScalarExecuting(DbCommand command,
CommandEventData eventData, InterceptionResult<object> result)
{
PatchCommandText(command);
return base.ScalarExecuting(command, eventData, result);
}
public override ValueTask<InterceptionResult<object>> ScalarExecutingAsync(
DbCommand command, CommandEventData eventData, InterceptionResult<object> result,
CancellationToken cancellationToken = default)
{
PatchCommandText(command);
return base.ScalarExecutingAsync(command, eventData, result, cancellationToken);
}
private void PatchCommandText(DbCommand command)
{
if (command.CommandText.StartsWith(_hintPrefix, StringComparison.Ordinal))
{
int index = command.CommandText.IndexOfAny(Environment.NewLine.ToCharArray(),
_hintPrefix.Length);
command.CommandText = _tableAliasRegex
.Replace(command.CommandText, "${0} WITH (" + command.CommandText
.Substring(_hintPrefix.Length, index - _hintPrefix.Length) + ")")
.Substring(index);
}
}
}
示例用法:
public static class QueryHintsDbContextOptionsBuilderExtensions
{
private const string HintTag = "Use hint: ";
public static IQueryable<T> WithHint<T>(this IQueryable<T> source, TableHint hint) =>
source.TagWith(HintTag + hint);
public static DbContextOptionsBuilder<TContext> AddQueryHints<TContext>(
this DbContextOptionsBuilder<TContext> builder)
where TContext : DbContext =>
builder.AddInterceptors(new QueryHintInterceptor(HintTag));
}
public enum TableHint
{
KeepIdentity,
KeepDefaults,
HoldLock,
Ignore_Constraints,
Ignore_Triggers,
Nolock,
NoWait,
PagLock,
ReadCommitted,
ReadCommittedLock,
ReadPast,
RepeatableRead,
RowLock,
Serializable,
Snapshot,
TabLock,
TabLockX,
UpdLock,
Xlock
}