Как настроить логическое выражение, которое может выполняться как на C#, так и на SQL, не завися от таких библиотек, как EntityFramework?

Я хочу иметь приложение, в котором пользователь (обычно опытный пользователь) может вводить логическое выражение. Я хочу иметь возможность выполнять логическое выражение как в .NET, так и в SQL.

Сами выражения не так уж сложны, это такие вещи, как:

  • страна Соединенные Штаты
  • страна одна из: сша, канада, мексика
  • (страна США) И (возраст 20 лет)
  • (страна — США) ИЛИ ((возраст — 20 лет) и страна — одна из: США, Канада)

Мне нужно иметь возможность поддерживать базовые вещи, такие как «в», «равно», «больше/меньше», «между», «содержит», «начинается с» и т. д. Я хочу иметь возможность компилировать в С# и запускать его для объекта типа dynamic, а также быть в состоянии скомпилировать выражение в SQL. Я бы подключил результат к предложению where очень конкретного запроса.

Я не хочу использовать Nhibernate или EntityFramework, я хочу иметь возможность выполнять SQL напрямую.

ОБНОВЛЕНИЕ: я уже знаю, что хочу выполнить его с помощью ADO.NET. Извините, если я не ясно выразился. Я просто хочу знать, какой хороший подход был бы для хранения логического выражения, которое можно выполнить как на С#, так и на SQL. Меня не интересуют хранимые процедуры и параметризация (последняя очевидна и тривиальна, когда я могу сгенерировать запрос). Пользователи, вводящие выражения, являются внутренними для моей компании, в основном это разработчики и опытные пользователи.

Поэтому я думаю об использовании таких вещей, как деревья выражений, абстрактные синтаксические деревья, LINQ и т. д., для достижения этой цели. Я не хочу использовать ORM, но я хочу сделать что-то очень похожее на то, что делают ORM в своих выражениях LINQ, чтобы преобразовать лямбы в код для предложения WHERE.

ОБНОВЛЕНИЕ 2: пока мы думаем об этом, чтобы выражения вводились как C# и сохранялись в виде строк в базе данных. Когда мы хотим выполнить в контексте .NET, мы скомпилируем его в лямбду или выражение и, возможно, обернем его в объект, который будет интерфейсом с методом для представления логического выражения interface IDynamicFilter { bool PassesFilter<SomePocoType>(poco); }. Или мы могли бы поместить POCO в IEnumerable и запустить LINQ .Where() с переданным лямбда-выражением, чтобы получить объекты, соответствующие фильтрам.

Что касается SQL, это та часть, в которой я более нечеток. Мы хотим воспроизвести то, что делают ORM — посетить дерево выражений и преобразовать его в строку SQL. Нам не нужно поддерживать полный набор SQL. Нам нужно только поддерживать довольно простые операторы, такие как группировка со скобками, И/ИЛИ/НЕ, в/не в, GT/LT/EQ/NEQ/между. Мы также хотим поддерживать некоторые основные математические операции (a + b > c).


person DarkwingDev    schedule 08.03.2013    source источник
comment
выполнить sql напрямую... вы имеете в виду написать динамический запрос? Найдите ADO.NET. Похоже, вы хотите использовать SqlCommand. Просто убедитесь, что вы настроили его для защиты от атак SQL-инъекций.   -  person Jeremy Holovacs    schedule 08.03.2013
comment
Что вы собираетесь выполнять в С# и SQL? Я имею в виду, что будет представлять (возможные) значения страны, возраста и т. д.? В sql вы будете вставлять результат в предложение where очень конкретного запроса. Что насчет С#?   -  person Andrew Savinykh    schedule 08.03.2013
comment
Также вы не хотите использовать ORM, но хотите делать то, что делают ORM. Можете ли вы объяснить, почему?   -  person Andrew Savinykh    schedule 08.03.2013
comment
Нам нужна возможность конвертировать в SQL и использовать его в разных местах — консольные приложения, которые будут извлекать все данные, соответствующие каждому выражению. Другие рабочие процессы будут находить сущности, соответствующие выражениям, и выполнять над ними операции. В других случаях мы выгружали выражения в файлы шаблонов .sql, чтобы неспециалисты могли запускать их из инструмента, похожего на SSMS. Любой из этих запросов может иметь такие части, как извлечение данных со связанных серверов, шаги, которые генерируют временные таблицы, CTE, агрегации и т. д.   -  person DarkwingDev    schedule 08.03.2013
comment
@zespri, в C# я бы выполнял либо POCO, dynamic, либо словарь строк, в зависимости от того, что работает лучше. Мы еще не разработали эту часть, поэтому я открыт для предложений.   -  person DarkwingDev    schedule 08.03.2013


Ответы (2)


В Microsoft CRM есть что-то вроде этого, где они дают вам поля формы и раскрывающийся список, обозначающий, что вы пытаетесь найти, и какой у вас логический оператор, так что вы можете иметь.

Column:
Select Column in Database

Opeartor: 
>
<
>=
<=
LIKE
=
IN
NOT IN

Value:
TextBox for user input

Таким образом, вы бы взяли пользовательский ввод и просто создали запросы на его основе. Если вы не хотите использовать ORM, например Entity, вы можете использовать ADO.NET.

person Jack Marchetti    schedule 08.03.2013

Если я понимаю вашу проблему, вы можете сделать что-то вроде этого (и имейте в виду, что я не рекомендую такие вещи для производства):

var query = "SELECT * FROM TABLE as T WHERE 1=1";

if ([some condition]) query += " AND T.CountryCode = 'USA'";
if ([some other condition]) query += " AND T.CountryCode IN ('USA', 'CAN', 'MEX')";
if ([yet another condition]) query += " AND T.CountryCode = 'USA' AND T.Age = 20";

using (var conn = new SqlConnection(connectionString))
{
    conn.Open();
    using (var comm = new SqlCommand(conn, query))
    {
        var results = comm.ExecuteReader();  //returns an IDataReader you can loop through.
    }
}

Я повторю еще раз, убедитесь, что вы параметризуете любые переменные, которые вы ищете, такой запрос довольно уязвим для внедрения sql. Это способ ленивого разработчика сделать что-то, и он может быть довольно опасным. Если вы хотите избежать ORM, такая логика должна быть в хранимой процедуре, которой вы передаете параметры.

person Jeremy Holovacs    schedule 08.03.2013
comment
Я не могу жестко закодировать if, это не то, что я пытаюсь сделать. Я хочу динамически хранить логические выражения в базе данных. Затем я хочу иметь возможность выполнять это определение в контексте C#, а также в контексте SQL. Свойства таблицы при выполнении в SQL всегда будут соответствовать свойствам объекта C#, свойства которого проверяются в логическом выражении. Я не знаю свойств во время компиляции. - person DarkwingDev; 08.03.2013
comment
Это не то, что вы действительно можете сделать ... теоретически вы выполняете мощный динамический SQL в своей базе данных, но я не могу представить ситуацию, когда это было бы желательным планом действий. ближайший практический эквивалент для этого - написание некоторых хранимых процедур/представлений для ваших данных. Это не будет динамично на том уровне, на который вы рассчитываете, но это считается гораздо лучшей практикой по множеству причин. - person Jeremy Holovacs; 08.03.2013
comment
Извините, но я не согласен с вами в том, что это невозможно сделать, если вы не объясните, почему. Тот факт, что LINQ to SQL вообще существует, является доказательством того, что это было сделано. Это было сделано в ENtityFramework, и в NHIbernate, и даже в клиенте MongoDB C# (за исключением того, что компилируется в запросы MongoDB, а не в запросы SQL). Я хочу того же, что и они, но с возможностью просто генерировать код SQL в строку без того, чтобы эти фреймворки выполняли его для меня. - person DarkwingDev; 08.03.2013
comment
Эммм... нет, не было. Во всех ваших примерах (кроме Mongo, я не могу с ним говорить, никогда не использовал его раньше) в базе данных нет динамического логического пула, на который вы ссылаетесь. В объектных моделях ORM есть лямбда-выражения, но они не имеют абсолютно никакого отношения к базе данных. В этих случаях кто-то написал интерфейс в коде, чтобы в основном делать то, что я отвечаю... за исключением параметризованных запросов. Функционал, о котором вы говорите, находится исключительно в ORM. - person Jeremy Holovacs; 08.03.2013
comment
Когда вы используете LINQ для запроса данных в чем-то вроде NHibernate, лямбда-выражения, которые вы пишете для определения вашего WHERE (наряду с select, group by и т. д., но меня интересует только where), компилируются в SQL. Это то, что я хочу. Вы можете скомпилировать строку в лямбда-выражение или дерево выражений, а можете скомпилировать лямбда-выражение в SQL-запрос. Если строка, которую я хочу преобразовать, — это C#, и мне нужно скомпилировать ее прямо в лямбду, а затем использовать процесс преобразования для компиляции из лямбды в SQL, это сработает. Мне кажется, вы не понимаете, о чем я спрашиваю, или я неправильно спрашиваю. - person DarkwingDev; 08.03.2013
comment
Я пытаюсь сказать вам, что компиляция в SQL является частью ORM и предназначена для внутренней обработки. Я не думаю, что вы можете получить то, что ищете, без использования картографа, такого как LINQ to SQL, NHibernate или Entities Framework... если только вы не напишете свой собственный. - person Jeremy Holovacs; 08.03.2013
comment
Тяжело не то же самое, что невозможно. - person DarkwingDev; 08.03.2013
comment
Что ж, если вы хотите Hard, вы можете загрузить исходный код для NHibernate и разобраться, как они создают SQL. Я не думаю, что L2SQL или EF позволят вам просмотреть исходный код. - person Jeremy Holovacs; 08.03.2013
comment
Джереми, я хочу извиниться, если я показался грубым. Я просто не в хорошем настроении сегодня, и я ценю, что вы пытаетесь помочь ответить на мой вопрос. - person DarkwingDev; 08.03.2013