2

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

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

所以我将执行一个 3 级循环。在每个“配置”级别,我将启动一个数据库事务并编译论坛,每个“项目”将使用相同的事务 + 编译公式(每个配置大约有 20 个公式,每个项目将使用所有公式)。

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

有什么建议么?

--更新--这就是我的选择,谢谢! http://www.codeproject.com/Articles/53611/Embedding-IronPython-in-aC-Application

4

5 回答 5

2

Iron Python允许您将脚本引擎嵌入到您的应用程序中。还有许多其他解决方案。实际上,您可以在 Google 上搜索“C# 嵌入式脚本”之类的内容并找到一大堆选项。有些比其他更容易集成,有些比其他更容易编写脚本。

当然,总是有 VBA。但这简直是丑陋的。

于 2010-05-18T13:16:16.443 回答
1

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

于 2010-05-18T13:19:01.733 回答
1

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

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

这工作了一段时间,直到我们需要一个公式引用另一个公式的能力,而 SQL Server 不允许这样做。所以我切换到脚本引擎。IronPython 那时还不是很成熟,所以我选择了另一个引擎……我现在不记得是哪个了。总之,写起来很容易,就是有点慢。不是很多,每个查询可能只有几毫秒,但对于 Web 应用程序来说,时间真的加起来了所有请求。

那是我决定为公式编写自己的解析器的时候。也就是说,我有一个 PlusToken 类来添加两个值,一个 ItemToken 类对应于 GetValue("Discount") 等。当用户输入新公式时,验证器会解析公式,确保它有效(例如,他们是否引用了一个不存在的列?),并将其存储在一个半编译的形式中,以便以后解析。当用户请求计算值时,解析器读取公式,对其进行解析,从数据库中找出需要哪些数据,并计算出最终答案。前期做了很多工作,但效果很好,而且速度非常快。这是我学到的:

  1. 如果用户输入的公式导致公式中的循环,并且您尝试计算公式的值,您将用完堆栈空间。如果您在 Web 应用程序上运行它,整个 Web 服务器将停止工作,直到您重置它。因此,在验证阶段检测周期很重要。
  2. 如果您有多个公式,请将所有数据库调用汇总到一个位置,然后一次请求所有数据。快多了。
  3. 用户会将古怪的东西输入公式。提供有用的错误消息的解析器将在以后省去很多麻烦。
于 2010-05-18T14:52:44.310 回答
1

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

然后你可以完全控制你的引用,你总是在 C# 中,所以你不需要担心内存管理(比你通常做的更多)等。IMO Antlr是在 C# 中执行此操作的最佳工具。您可以从该站点获得小语言的示例,例如您的场景。

但是......如果这真的只是一个开始,并且在最后您需要一种适当的脚本语言的几乎全部功能,那么您需要将脚本语言嵌入到您的系统中。使用您的数字,您将在性能、内存管理和可能的引用方面遇到问题,正如您所指出的。有几种方法,但我真的不能为你的场景给出一个建议:我从来没有以这样的规模做过。

于 2010-05-18T15:01:06.947 回答
0

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

于 2010-05-18T14:14:22.073 回答