如何允许用户在C#应用程序中定义财务公式

时间:2010-05-18 13:10:36

标签: c# scripting expression formula evaluator

我需要允许我的用户能够定义将根据数据计算值的公式。例如

//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. 项目(每个客户配置约10,000个)
  4. 所以我将执行3级循环。在每个“配置”级别,我将启动数据库事务并编译论坛,每个“项目”将使用相同的事务+编译公式(每个配置大约有20个公式,每个项目将使用所有这些)。

    这进一步使事情复杂化,因为我不能只使用编译器服务,因为它会导致持续的内存使用增长。我不能为每个“配置”循环级别使用新的AppDomain,因为我需要传递的一些引用无法编组。

    有什么建议吗?

    - Update-- 这就是我的意思,谢谢! http://www.codeproject.com/Articles/53611/Embedding-IronPython-in-a-C-Application

5 个答案:

答案 0 :(得分:2)

Iron Python允许您将脚本引擎嵌入到您的应用程序中。还有许多其他解决方案。事实上,你可以谷歌像“C#嵌入式脚本”,并找到一大堆选项。有些比其他人更容易集成,有些比其他人更容易编写脚本。

当然,总有VBA。但那简直太丑了。

答案 1 :(得分:1)

您可以在运行时创建一个简单的类,只需将您的逻辑写入字符串等,编译它,运行它并使其返回您需要的计算。本文向您展示如何从运行时访问编译器:http://www.codeproject.com/KB/cs/codecompilation.aspx

答案 2 :(得分:1)

几年前我遇到了类似的问题。我有一个中等流量的网络应用程序,需要允许方程式,它需要类似的功能,你必须快速。我经历了几个想法。

第一个解决方案涉及将计算列添加到我们的数据库中。我们的应用表存储列中的属性(例如,有一个应付金额列,另一个折扣等)。如果用户输入类似PropertyA * 2的公式,则代码将更改基础表以具有新的计算列。就添加和删除列而言,它很混乱。它确实有一些优点:数据库(SQL Server)在进行计算时非常快;数据库为我们处理了很多错误检测;我可以假装计算出的值与非计算值相同,这意味着我不必修改任何使用非计算值的现有代码。

这种方法有一段时间,直到我们需要公式引用另一个公式的能力,而SQL Server不允许这样做。所以我切换到脚本引擎。 IronPython当时不是很成熟,所以我选择了另一个引擎......我现在不记得是哪一个。无论如何,这很容易写,但它有点慢。不是很多,每个查询可能只有几毫秒,但对于一个Web应用程序来说,时间真的累积了所有请求。

那时我决定为公式编写自己的解析器。也就是说,我有一个PlusToken类来添加两个值,一个与GetValue(“Discount”)对应的ItemToken类等。当用户输入一个新的公式时,验证器会解析公式,确保它有效(如,他们引用了一个不存在的列吗?),并将其存储在半编译的形式中,以后很容易解析。当用户请求计算值时,解析器读取公式,解析公式,确定数据库中需要哪些数据,并计算最终答案。它预先花了相当多的工作,但它运作良好,而且速度非常快。这是我学到的东西:

  1. 如果用户输入导致公式中循环的公式,并且您尝试计算公式的值,则将耗尽堆栈空间。如果您在Web应用程序上运行此操作,整个Web服务器将停止工作,直到您重置它。因此,在验证阶段检测循环非常重要。
  2. 如果您有多个公式,请在一个位置聚合所有数据库调用,然后一次请求所有数据。快得多。
  3. 用户将古怪的东西输入公式。提供有用错误消息的解析器将在以后节省许多麻烦。

答案 3 :(得分:1)

如果自定义脚本没有比上面显示的更复杂,我会同意Sylvestre:创建自己的解析器,创建一个树并自己完成逻辑。您可以生成.Net expression tree或只是自己浏览语法树,并在您自己的代码中进行操作(下面的Antlr将帮助您生成此类代码)。

然后你可以完全控制你的引用,你总是在C#中,所以你不必担心内存管理(比你通常做的更多)等.IMO Antlr是最好的在C#中执行此操作的工具。您可以从网站获取小语言的示例,例如您的方案。

但是......如果这只是一个开始,最后你需要几乎完全掌握一种适当的脚本语言,你需要将脚本语言嵌入到你的系统中。使用您的数字,您将遇到性能,内存管理问题以及您提到的引用问题。有几种方法,但我不能给你的方案提供一个建议:我从来没有这么大规模地做过。

答案 4 :(得分:0)

您可以构建两个基类UnaryOperator(if,square,root ...)和BinaryOperator(+ - / *)并从表达式构建一个树。然后评估每个项目的树。