Скомпилированный запрос LINQ & DataLoadOptions... с изюминкой!
Я знаю о методе, обсуждаемом здесь:
... но это не работает для моей ситуации, когда я получаю:
Msgstr "Установка параметров загрузки не разрешена после того, как результаты были возвращены из запроса."
Я использую сценарии Codesmith PLINQO для генерации сущностей и кода менеджера, и код менеджера выглядит примерно так:
public partial class SearchManager
{
#region Query
// A private class for lazy loading static compiled queries.
private static partial class Query
{
internal static readonly Func<MyDataContext,IOrderedQueryable<Search>>
GetAll = CompiledQuery.Compile(
(MyDataContext db) =>
from s in db.Search
orderby s.Name
select s);
}
#endregion
public IQueryable<Search> GetAll()
{
return Query.GetAll(Context);
}
}
Сначала я попытался добавить статические DataLoadOptions в класс Searchmanager следующим образом:
public static readonly DataLoadOptions MyOptions =
(new Func<DataLoadOptions>(() =>
{
var option = new DataLoadOptions();
option.LoadWith<Search>(x => x.Rule);
return option;
}))();
... затем предоставляя его в контекст в методе GetAll, например:
public IQueryable<Search> GetAll()
{
Context.LoadOptions = MyOptions;
return Query.GetAll(Context);
}
... и это дало мне ошибку, которую я отметил выше. Это потому, что запрос уже скомпилирован, и поэтому не может быть добавлено "лишних" DataLoadOptions? Если это так, как можно применить DataLoadOptions до того, как запрос будет скомпилирован?
4 ответа
В свойстве setter класса DataContext есть условие, которое проверяет, есть ли у DataContext какие-либо объекты в его кэше, и значение LoadOptions НЕ равно null, а экземпляр LoadOptions, который вы пытаетесь установить, не совпадает с уже установленным. тогда вы получите это исключение.
Альтернатива № 1. Создать новый контекст для каждого запроса (вероятно, не очень хорошая идея)
Альтернатива № 2. Вызовите метод ClearCache с помощью отражения, затем статически создайте новые LoadOptions, назначьте их в Context и, наконец, получите скомпилированный запрос.
public partial class SearchManager
{
#region Query
// A private class for lazy loading static compiled queries.
private static partial class Query
{
internal static readonly Func<MyDataContext,IOrderedQueryable<Search>> GetAll
{
get {
return CompiledQuery.Compile(
(MyDataContext db) =>
from s in db.Search
orderby s.Name
select s);
}
}
#endregion
public IQueryable<Search> GetAll()
{
Context.ClearCache();
Context.LoadOptions = MyOptions;
return Query.GetAll(Context);
}
public static readonly DataLoadOptions MyOptions =
(new Func<DataLoadOptions>(() => MakeLoadOptions<Search>(x=>x.Rule)))();
}
public static class Extensions {
public static void ClearCache(this DataContext context)
{
const BindingFlags FLAGS = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
var method = context.GetType().GetMethod("ClearCache", FLAGS);
method.Invoke(context, null);
}
public static DataLoadOptions MakeLoadOptions<TEntity, TResult>(this Expression<Func<TEntity,TResult>> func) {
DataLoadOptions options = new DataLoadOptions();
options.LoadWith(func);
return options;
}
}
Само сообщение об ошибке говорит вам, что именно не так. Вы не можете применять DataLoadOptions после того, как запрос Linq вернул результаты. Или, может быть, лучший способ сказать это заключается в следующем. Если вы хотите применить DataLoadOptions, сделайте это перед выполнением запроса. Вы не можете сделать это потом.
Вы можете установить параметры загрузки только один раз для скомпилированного запроса. Ошибка должна быть сброшена при втором вызове. Переместите назначение в статический конструктор, и это должно решить вашу проблему.
public IQueryable<Search> GetAll() {
Context.LoadOptions = MyOptions;
return Query.GetAll(Context);
}
Это назначение слишком поздно, если Контекст уже возвратил какие-либо результаты запроса. Это не имеет ничего общего с скомпилированными запросами и связано с назначением свойства LoadOptions объекта DataContext. К сожалению, такое поведение свойства LoadOptions не задокументировано в msdn.