2010-05-18 2 views
1

Мне нужно разрешить моим пользователям определять формулы, которые будут вычислять значения на основе данных. НапримерКак разрешить пользователям определять финансовые формулы в приложении C#

//Example 1 
return GetMonetaryAmountFromDatabase("Amount due") * 1.2; 
//Example 2 
return GetMonetaryAmountFromDatabase("Amount due") * GetFactorFromDatabase("Discount"); 

мне нужно будет разрешить/* + - операции, а также назначить локальные переменные и выполнять IF заявления, как и

var amountDue = GetMonetaryAmountFromDatabase("Amount due"); 
if (amountDue > 100000) return amountDue * 0.75; 
if (amountDue > 50000) return amountDue * 0.9; 
return amountDue; 

Сценарий является сложным, потому что имеют следующую структуру. .

  1. клиентов (несколько сотен)
  2. конфигурации (около 10 на одного клиента)
  3. Пункт (около 10000 на каждую конфигурацию клиента)

Итак, я выполню петлю уровня 3. На каждом уровне «Конфигурация» я запустил транзакцию БД и скомпилировал форумла, каждый «Предмет» будет использовать те же транзакционные + скомпилированные формулы (на каждую конфигурацию должно быть 20 формул, каждый элемент будет использовать их все).

Это еще больше усложняет ситуацию, потому что я не могу просто использовать службы компилятора, так как это приведет к продолжению использования памяти. Я не могу использовать новый AppDomain для каждого цикла «Конфигурация», потому что некоторые из ссылок, которые мне нужно передать, не могут быть распределены.

Любые предложения?

--Update-- Это то, с чем я пошел, спасибо! http://www.codeproject.com/Articles/53611/Embedding-IronPython-in-a-C-Application

+0

Downvoted для неработающей ссылки. –

+0

Спасибо. Ссылка исправлена. Теперь вы можете удалить свое голосование :) –

ответ

1

Вы можете создать простой класс во время выполнения, просто записав свою логику в строку или т. П., Скомпилируйте ее, запустите и верните необходимые вычисления. В этой статье показано, как получить доступ к компилятору из среды выполнения: http://www.codeproject.com/KB/cs/codecompilation.aspx

+0

Как я уже сказал в тексте вопроса, я не могу использовать службу компилятора, потому что он создает сборку, которую я тогда не могу выгрузить, и я не могу создать новый домен приложения, потому что .net не может маршалировать типы, которые мне нужно передать. –

+0

Я принял ваш ответ.Решение состояло в том, чтобы иметь классы-оболочки вокруг моих несериализуемых. –

2

Iron Python Позволяет встроить скриптовый движок в приложение. Существует много других решений. На самом деле, вы можете сделать что-то вроде «C# embedded scripting» и найти целую кучу опций. Некоторые из них легче других интегрировать, а некоторые из них легче других кодировать сценарии.

Конечно, всегда есть VBA. Но это просто безобразно.

+0

Вот лучшая ссылка: http://www.voidspace.org.uk/ironpython/embedding.shtml – dviljoen

+1

Найден другой, который выглядит интересным: http://www.csscript.net/ – dviljoen

0

Вы можете создать два базовых класса UnaryOperator (if, square, root ...) и BinaryOperator (+ -/*) и построить дерево из выражения. Затем оцените дерево для каждого элемента.

1

Я столкнулся с аналогичной проблемой несколько лет назад. У меня было веб-приложение с умеренным трафиком, которое должно было позволить уравнениям, и для этого вам нужны были подобные функции, и это должно было быть быстрым. Я рассмотрел несколько идей.

Первое решение подразумевало добавление расчетных столбцов в нашу базу данных. Наши таблицы для приложения хранят свойства в столбцах (например, есть столбец «Сумма задолженности», еще одна скидка и т. Д.). Если пользователь ввел формулу, такую ​​как PropertyA * 2, код изменит базовую таблицу на новый расчетный столбец. Это бесполезно для добавления и удаления столбцов.Он имеет несколько преимуществ: база данных (SQL Server) была очень быстрой при выполнении вычислений; база данных обработала много ошибок для нас; и я мог бы притворяться, что вычисленные значения совпадают с не рассчитанными значениями, а это значит, что мне не пришлось изменять какой-либо существующий код, который работал с невычислимыми значениями.

Это работало некоторое время, пока нам не понадобилась возможность для формулы ссылаться на другую формулу, и SQL Server этого не допускает. Поэтому я переключился на скриптовый движок. Тогда IronPython был не очень зрелым, поэтому я выбрал еще один движок ... Я не могу вспомнить, какой именно сейчас. Во всяком случае, было легко писать, но это было немного медленнее. Не много, может быть, несколько миллисекунд на запрос, но для веб-приложения время действительно добавлено по всем запросам.

Именно тогда я решил написать свой собственный парсер для формул. То есть у меня есть класс PlusToken для добавления двух значений, класса ItemToken, который соответствует GetValue («Скидка») и т. Д. Когда пользователь вводит новую формулу, валидатор анализирует формулу, делает ее действительной (например, ссылались ли они на столбец, который не существует?) и хранит его в полукомпилированной форме, которую легко проанализировать позже. Когда пользователь запрашивает вычисленное значение, синтаксический анализатор считывает формулу, анализирует ее, вычисляет, какие данные необходимы из базы данных, и вычисляет окончательный ответ. Для этого потребовалось много работы, но она работает хорошо, и это очень быстро. Вот что я узнал:

  1. Если пользователь вводит формулу, которая приводит к циклу в формулах, и вы пытаетесь вычислить значение формулы, у вас закончится пространство стека. Если вы используете это в веб-приложении, весь веб-сервер перестанет работать, пока вы его не сбросите. Поэтому важно определить циклы на этапе проверки.
  2. Если у вас более двух формул, суммируйте все вызовы базы данных в одном месте, а затем запросите все данные одновременно. Намного быстрее.
  3. Пользователи будут вводить пустые вещи в формулы. Парсер, который предоставляет полезные сообщения об ошибках, сэкономит много головных болей позже.
1

Если пользовательские сценарии не становятся более сложными, чем те, которые вы показываете выше, я согласен с Sylvestre: создайте свой собственный парсер, создайте дерево и сделайте логику самостоятельно. Вы можете сгенерировать .Net expression tree или просто пройти через дерево синтаксиса самостоятельно и выполнить операции в своем собственном коде (Antlr ниже поможет вам создать такой код).

После этого вы полностью контролируете свои ссылки, вы всегда находитесь на C#, поэтому вам не нужно беспокоиться об управлении памятью (больше, чем вы обычно делаете) и т. Д. IMO Antlr - лучший инструмент для выполнения это в C#. Вы получаете примеры с сайта для небольших языков, например, ваш сценарий.

Но ... если это действительно просто начало, и в конце вам нужно почти полное использование правильного языка сценариев, вам нужно будет внедрить скриптовый язык в вашу систему. С вашими номерами у вас будет проблема с производительностью, управлением памятью и, возможно, ссылками, как вы заметили. Существует несколько подходов, но я не могу дать ни одной рекомендации для вашего сценария: я никогда не делал этого в таком масштабе.