Примечание: Это старый вопрос. С Линкпад 5 у вас были ограничения, которые я описал ниже, потому что он не написан в .NET Core. Но теперь вы можете использовать более новый Линкпад 6 или 7, который полностью поддерживает Entity Framework Core, включая драйверы для нескольких систем баз данных.
Я хочу анализировать запросы Entity Framework Core,, который я скопировал из существующего кода в LinqPad, например
UserQuery this_context;
void Main()
{
this_context = this;
var validationResult = (
from ProjectInfo pi in this_context.GetAll<ProjectInfo>()
join GeneralInformation gi in this_context.GetAll<GeneralInformation>()
on pi.Id equals gi.Id into gpi
from gps in gpi.DefaultIfEmpty()
select new { ... }).ToList();
}
Я знаю, что Linqpad не поддерживает .NET Core,, поэтому мне нужно немного его настроить - для этого я создал метод расширения следующим образом:
public static class Ext
{
public static IEnumerable GetAll<T>(this UserQuery uq)
where T: new()
{
return GetAll(uq, new T());
}
private static IEnumerable GetAll<T>(this UserQuery uq, T a)
{
switch (a)
{
case ProjectInfo v:
return uq.ProjectInfo.Select(s => s);
case GeneralInformation v:
return uq.GeneralInformation.Select(s => s);
// ... many more cases like the above
default: // unknown type
throw new TypeAccessException($"Unsupported type {a.GetType().FullName}");
}
}
}
Для получения типа требуется перегруженная функция GetAll
— она просто создает пустой новый объект и передает его, чтобы оператор switch мог правильно вывести правильный тип и вернуть правильный запрос.
Это прекрасно работает, но много усилий вставлять каждый тип в оператор switch — это много усилий.
Итак, мой вопрос:
Как этого можно достичь более разумным способом, т. е. без необходимости указывать каждый отдельный тип сущности?
Объект LINQPad UserQuery
, похоже, имеет именно тот метод, который вам нужен:
public static class Ext {
public static IEnumerable GetAll<T>(this UserQuery uq) =>uq.GetTable(typeof(T));
}
Я считаю, что вы могли бы оставаться в безопасности с помощью:
public static IQueryable<T> GetAll<T>(this UserQuery uq) => (IQueryable<T>)uq.GetTable(typeof(T));
Использование вашей исходной версии IEnumerable
приводит к тому, что все таблицы загружаются в память одна за другой, а затем LINQ выполняется для объектов. Использование IQueryable<T>
переводит запрос в SQL — это может быть нежелательно в тех случаях, когда перевод LINQ to SQL и EF различается (без уважительной причины), но я мог видеть проблемы со специфическими возможностями EF (например, Include
и DbFunctions
, хотя у меня есть написаны пустые Include
методы расширения, чтобы скрыть некоторые).
Если версия IQueryable
не работает, версия ITable<>
может:
public static ITable<T> GetAll<T>(this UserQuery uq) where T : class => (ITable<T>)uq.GetTable(typeof(T));
Возможно, после .AsQueryable()
нужен GetTable
? Я не получаю сообщение об ошибке, когда тестирую его с помощью подключения к MS SQL. Я добавил еще одну версию typesafe, которая может работать лучше.
Обе типизированные версии выдают InvalidOperationException: Could not translate expression 'Table(ProjectInfo).Cast().GroupJoin(Table(GeneralInformation).Cast(), pi => pi.Id ...
в моем случае, а нетипизированная версия (1-я в вашем ответе) работает нормально
Наконец-то я нашел способ заставить его работать с типизированным результатом: public static IEnumerable<T> GetAll<T>(this UserQuery uq) => (IEnumerable<T>)uq.GetTable(typeof(T));
- это работает нормально, поэтому причина в том, что классы интерфейса IQueryable<T>
и ITable<T>
не на 100% совместимы с Linq.
@ Matt Я не думаю, что это правильно - IQueryable<T>
является основой LINQ (для SQL / EF). Если вы приведете к IEnumerable<T>
, я буду обеспокоен тем, что вы загружаете всю таблицу в память и выполняете не SQL-запрос, а скорее LINQ to Objects, которые могут немного отличаться.
Это только для быстрого анализа в Linqpad, а не для целей оптимизации. Я согласен, что предпочел бы IQeryable<T>, но для моего запроса это не работает.
Работает отлично, это было именно то, что я искал. Большое спасибо! Однако типобезопасная версия выдает InvalidOperationException по неизвестной причине.