如何在Entity Framework中指定索引提示?

时间:2011-11-06 22:58:40

标签: c# sql-server linq entity-framework c#-4.0

SQL

select * from table1 with(index=IX_table1_1)

使用ado.net实体的Linq to sql想要编写上面的代码。我特别找不到实体,使用索引。

LINQ

var querysample = from a in db.table1
select a;

4 个答案:

答案 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
}