2010-05-18 58 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. 项目(每个客户配置约10,000)

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

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

有什么建议吗?

- 更新 - 这是我一起去的,谢谢! http://www.codeproject.com/Articles/53611/Embedding-IronPython-in-a-C-Application

+0

Downvoted for broken link。 – 2014-11-01 02:18:09

+0

谢谢。链接固定。你现在可以删除你的投票:) – 2014-11-01 10:30:46

回答

1

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

+0

正如我在问题文字中所说的,我无法使用编译器服务,因为它创建了一个程序集,然后我无法卸载,并且我无法创建新的应用程序域,因为.net无法封送我需要传递给它的类型。 – 2010-05-18 13:43:41

+0

我接受了你的回答。解决方案是在我的不可序列化的类中使用包装类。 – 2014-11-03 14:36:21

2

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

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

+0

这里有一个更好的链接: http://www.voidspace.org.uk/ironpython/embedding.shtml – dviljoen 2010-05-18 13:17:20

+1

找到另一个看起来很有趣的: http://www.csscript.net/ – dviljoen 2010-05-18 17:29:06

0

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

1

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

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

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

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

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

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

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

但是......如果这真的只是一个开始,并且最终需要几乎所有正确的脚本语言的全部力量,那么您需要将脚本语言嵌入到系统中。用你的数字,你会遇到性能问题,内存管理问题,并可能引用你提到的问题。有几种方法,但我不能给你的场景提供一个建议:我从来没有这样做过。

相关问题