Как разрешить пользователям определять финансовые формулы в приложении C # - PullRequest
1 голос
/ 18 мая 2010

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

//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

Ответы [ 5 ]

2 голосов
/ 18 мая 2010

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

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

1 голос
/ 18 мая 2010

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

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

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

1 голос
/ 18 мая 2010

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

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

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

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

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

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

0 голосов
/ 18 мая 2010

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

...