#c# #entity-framework-core #linqpad
#c# #entity-framework-core #linqpad
Вопрос:
Примечание: Это старый вопрос. С LINQPad 5 у вас были ограничения, которые я описал ниже, потому что он не написан на .NET Core. Но теперь вы можете использовать более новый LINQPad 6 или 7, который полностью поддерживает Entity Framework Core, включая драйверы для нескольких систем баз данных.
Я хочу проанализировать основные запросы Entity Framework, которые я скопировал из существующего кода в 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.
Итак, мой вопрос:
Как этого можно достичь более разумным способом, т. Е. Без необходимости указывать каждый отдельный тип объекта?
Ответ №1:
UserQuery
Объект LINQPad, похоже, имеет метод, который именно то, что вы хотите:
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));
Комментарии:
1. Работает нормально, это было именно то, что я искал. Большое спасибо! Однако типобезопасная версия выдает исключение InvalidOperationException по неизвестной причине.
2. Возможно
.AsQueryable()
, послеGetTable
? Я не получаю сообщение об ошибке, когда тестирую его, используя подключение к MS SQL. Я добавил еще одну версию typesafe, которая может работать лучше.3.
InvalidOperationException: Could not translate expression 'Table(ProjectInfo).Cast().GroupJoin(Table(GeneralInformation).Cast(), pi => pi.Id ...
В моем случае используются обе типизированные версии, в то время как нетипизированная версия (1-я в вашем ответе) работает нормально4. Я наконец нашел способ заставить его работать с типизированным результатом:
public static IEnumerable<T> GetAll<T>(this UserQuery uq) => (IEnumerable<T>)uq.GetTable(typeof(T));
— это работает нормально, поэтому причина заключалась в том, что классы интерфейсаIQueryable<T>
иITable<T>
не на 100% совместимы с Linq.5. @Matt Я не думаю, что это правильно —
IQueryable<T>
это основа LINQ (для SQL / EF). Если вы приведете кIEnumerable<T>
I, я был бы обеспокоен тем, что вы загружаете всю таблицу в память и выполняете не SQL-запрос, а LINQ к объектам, которые могут быть немного разными.