#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);
}