Как я могу «разогреть» предварительно скомпилированный запрос LinqToSql?

#c# #linq-to-sql #compiled-query

#c# #linq-to-sql #скомпилированный запрос

Вопрос:

В LinqToSql предварительно скомпилированные запросы великолепны, но мне все равно требуется, чтобы производительность компиляции снизилась при первом использовании запроса.

Я хотел бы «разогреть» эти предварительно скомпилированные запросы в фоновом режиме при запуске приложения. Очевидно, что я могу сделать это, вызвав их с некоторыми параметрами по умолчанию — однако это приводит к ненужному попаданию в базу данных.

Есть ли какой-либо способ «разогреть» предварительно скомпилированный запрос без обращения к базе данных?

Я взглянул на исходный код CompliedQuery, но, похоже, многие из необходимых классов являются закрытыми и / или внутренними…

Комментарии:

1. Это зависит от того, что вызывает «попадание». Вы пробовали предварительно генерировать представления? (обратите внимание, НЕ представления MVC) msdn.microsoft.com/en-us/data/dn469601.aspx

2. «попадание» — это linq to sql, переводящий дерево выражений в SQL. Другими словами, это сама цель использования CompiledQuery. Compile() 🙂

3. Я не понимаю, почему опция CompliedQuery isn’t?

4. @ShaunRowan — вы уверены в этом? И я не просто имею в виду, что вы думаете, что знаете .. у вас есть доказательства от профилировщика или что-то, что показывает, что это то, где находится хит? Если вы посмотрите на следующую статью, она показывает, где находятся проблемы с производительностью, и генерация представлений является одним из них. msdn.microsoft.com/en-us/data/hh949853.aspx перевод запроса есть, но не компиляция.

5. Я уверен. Попадание находится в дереве выражений visitors, которое создает SQL. Его очень легко воссоздать — создайте практически любой скомпилированный запрос средней и высокой сложности и измерьте производительность. В первый раз это займет больше времени, независимо от того, прогрели ли вы свой контекст (в данном случае это не имеет ничего общего с генерацией представлений, которая на самом деле является концепцией Entity Framework и не имеет ничего общего с linq to sql)

Ответ №1:

Хорошо, итак, просмотрев исходный код для CompiledQuery того, который возвращается, CompiledQuery.Compile() мы можем убедиться, что запрос фактически скомпилирован только после первого вызова метода:

     ICompiledQuery compiled; 
    private object ExecuteQuery(DataContext context, object[] args) {
        if (context == null) { 
            throw Error.ArgumentNull("context");
        } 
        if (this.compiled == null) { 
            lock (this) {
                if (this.compiled == null) { 
                    // This is where the query is copmiled
                    this.compiled = context.Provider.Compile(this.query);
                    this.mappingSource = context.Mapping.MappingSource;
                }
            } 
        }
        // this is where the query is executed against the data store
        return this.compiled.Execute(context.Provider, args).ReturnValue;
    }
 

Таким образом, нет способа принудительно выполнить компиляцию без ее фактического выполнения.

Тем не менее, я нашел для этого хитрый обходной путь, который дает мне то, что мне нужно. Существует способ, которым мы можем выполнить запрос, что приведет к предварительной компиляции запроса без разрешения вызова базы данных:

 // compiled query
static Func<NorthwindDataContext, int, IQueryable<Order>> GetOrderById =
    CompiledQuery.Compile((NorthwindDataContext db, int orderId) => LINQ GOES HERE );


static void Warmup() 
{
    var context = new NorthwindDataContext("ConnectionString");
    // dispose the connection to force the attempted database call to fail
    context.Connecction.Dispose(); 
    try  
    {
         GetOrderById(context, 1);
    }
    catch (Exception ex)
    {
         // we expect to find InvalidOperationException with the message
         // "The ConnectionString property has not been initialized."
         // but the underlying query will now be compiled
    }
}

static void GetData() 
{
     // as long as we called warmup first, this will not take the compilation performance hit
     var context = new NorthwindDataContext("ConnectionString");
     var result = GetOrderById(context, 1);
}