2016-11-20 4 views
0

У меня есть следующий сценарий:запрессовки таблицы динамически с чистым LINQ

  1. Там может быть два случайных таблицы с более чем 100 столбцов каждая.
  2. Одна из таблиц имеет внешний ключ для другого.
  3. Пользователь выбирает набор столбцов из обеих таблиц, которые мы должны выбрать из db и отправлять обратно как объект JSON. Ограничение состоит в том, что он должен быть чистым LINQ (а не DynamicLINQ).

Я попытался поиграть с выражениями по ответам, которые я смог найти, но лучшее, что я достиг, является IEnumerable в качестве результата, что неприемлемо, потому что мне нужно IQueriable для фильтрации позже. Я искал назад и вперед, но единственный рабочий вариант, который я нашел, был на DynamicSQL here, но мне не разрешено использовать его.

Любые идеи очень ценятся.

Обновление: В качестве примера у меня есть две случайные таблицы, соединенные fk, поэтому это просто регулярное соединение, например from t1 in Table1 join t2 in Table2 on t1.field1 = t2.field2. Все, что мне нужно - это передать выражение выбора этому соединению, основанное на коллекции строк, содержащих столбцы, которые я хочу выбрать, например, если у меня есть {"t1.field1", "t1.field2", "t2.field3"}, тогда соединение должно выглядеть как from t1 in Table1 join t2 in Table2 on t1.field1 = t2.field2 select new {t1.field1, t1.field2, t2.field3}.

+0

Начните свое соединение в режиме просмотра db. И сопоставьте это представление с вашими сущностями. – user1681317

+0

@ user1681317, как я уже сказал, будут случайные таблицы со случайным набором столбцов, поэтому мне нужно динамическое соединение с анонимным типом в select. Я довольно уверен, что это может быть достигнуто с помощью выражений, но мои знания в этой теме не идеальны, и я не смог реализовать это. – KorsaR

+0

Не могли бы вы рассказать о вашем запросе. Это поможет много, если вы предоставите свой фактический запрос и скажете нам, какие части вы хотите динамизировать (это слово?). – Sefe

ответ

0

Проблема с созданием экземпляров из списка столбцов заключается в том, что, поскольку .NET безопасен по типу, вам понадобится тип, который вы можете создать. Когда вы используете анонимные классы (т. Е. Ключевое слово new без имени класса), компилятор создаст для вас класс. Это анонимно, но он все еще там во время компиляции (вы можете проверить это в декомпиляторе по вашему выбору).

Если вы хотите по-настоящему быть полностью динамичным во время выполнения, вам придется динамически создавать и компилировать свои классы во время выполнения. Для этого вы можете посмотреть в пространствах имен System.CodeDom и System.Reflection.Emit, которые содержат классы, которые позволяют динамически создавать типы во время выполнения. Это, однако, будет довольно масштабным мероприятием, которое, по его мнению, будет стоить вашего времени. И затем вы хотите получить доступ к данным в этих объектах, поэтому вам, вероятно, придется перейти на переменные dynamic.

Что должно быть более целесообразным, так это создать обычный класс во время компиляции и создать его в запросе LINQ. Этот класс будет содержать все возможных свойств, которые вы можете установить. Когда вы создаете экземпляр, вам не нужно заполнять все поля.

Как только у вас есть свой класс, вы можете динамически создавать выражения, которые будут его создавать. Вот для чего предназначены классы в пространстве имен System.Linq.Expressions. Класс Expression содержит заводские методы, которые позволяют вам создать дерево выражений, в котором вы нуждаетесь.

Чтобы создать дерево выражений, сначала нужно разложить выражение, которое вы хотите моделировать.YOR выражение для Join будет выглядеть следующим образом (предполагается, что ваш класс контейнер с именем DataContainer):

(t1, t2) => new DataContainer { 
    Value1 = t1.field1, 
    Value2 = t1.field2, 
    Value3 = t2.field3 
} 

Это выражение должно быть разделен на его части в соответствии с их старшинства:

  • лямбда-выражения инициируется оператором лямбда =>: LambdaExpression, созданный Expression.Lambda
  • параметров t1 и t2 слева от оператора лямбда: ParameterExpression, созданный с Expression.Parameter
  • Объект экземпляра new DataContainer справа от оператора лямбда: NewExpression, созданный с Expression.New
  • заданий внутри блока инициализации, инициированной =: BinaryExpression, созданный с Expression.Assign
  • свойств к слева от задания: MemberExpression, созданные с Expression.Property
  • Свойства разыменования с .field1: MemberExpression, созданным с Expression.Property
  • Доступ параметр t1: ParameterExpression, созданный с Expression.Parameter (но вы повторно выражение параметра, который вы создали для лямбда-выражения)

Как вы можете видеть, это довольно утомительно по сравнению с просто писать вниз (или с помощью Dynamic LINQ). Я буду иллюстрировать это с подвыражением t1.field1:

На этом этапе будет создан параметром t1 для левой части лямбды:

ParameterExpression t1Param = Expression.Parameter(typeof(Table1), "t1"); 

Что вы повторно использовать для доступа к свойству:

MemberExpression t1field1Property = Expression.Property(t1Param, "field1"); 

Это выражение, которое вы используете при создании своего назначения, которое вы будете использовать вместе с другими присваиваниями в выражении-экземпляре, которое вы будете использовать для правой части выражения lamdba, вдоль w с другими необходимыми выражениями. Вы также можете записать это как одно дерево (за исключением выражений параметров, которые вы собираетесь использовать повторно).

Счастливое кодирование!

Смежные вопросы