使用CSharpCodeProvider允许用户创建函数

本文关键字:用户 创建 函数 CSharpCodeProvider 许用户 使用 | 更新日期: 2023-09-27 17:57:52

我有一个现有的asp.net(c#)应用程序。我需要为用户提供一种创建灵活规则的方法,以便在给定雇佣日期和注册日期的情况下计算生效日期。

可能使用的一些规则示例:

  1. 雇用日期或注册日期中较晚的一个
  2. 招聘日期+90天
  3. 报名日期后的第一个月
  4. 如果注册日期在当月15日之前,则生效日期为下个月的1日。如果是在15号或之后,那就是之后一个月的1号

一开始我使用了一些偏移字段(日偏移、月偏移等),但当我遇到新的需求时,我逐渐意识到当前的方法不够灵活。

我想做的是允许最终用户定义一个函数,该函数在给定两个参数(hirodate、enrollmentdate)的情况下返回日期,并将该函数存储在数据库中。当我需要计算有效日期时,我会从数据库中提取这个函数,执行它,传入参数以获得有效日期。

我最初的反应是寻找一个DSL,它可以让我定义日期操作功能,并将其集成到我的解决方案中。然而,我搜索合适的DSL却一无所获。

现在我想知道CSharpCodeProvider是否可以作为解决方案的一个组件。如果我从数据库中提取一个字符串,并通过CsharpCodeProvider进行编译,我是否可以强制结果代码与函数签名匹配(使用2个日期时间参数,并返回一个数据时间)?

有没有办法确保该功能没有任何副作用?例如,无I/O。没有读取或会话、缓存或应用程序。

使用CSharpCodeProvider允许用户创建函数

请参阅我最近的回答:解析"日期时间。现在"?

从本质上讲,您可以很容易地利用现有的库(如FLEE)来解析表达式并为这些规则发出IL。如果您看一下这些示例,您可以看到如何为用户表达式设置要利用的变量。例如,您可以定义一个由一些输入变量(如HireDateEnrollmentDate)和返回日期的用户表达式/谓词组成的"规则"。如果像我在链接答案中那样公开DateTime成员,那么用户也可以利用这些成员。

作为一个快速的例子,没有经过测试,但应该给你一个想法。

你可以设置一些自定义功能来提供帮助,比如获得一个月的第一天:

public static class CustomFunctions
{
    public static DateTime GetFirstDayOfMonth(DateTime date)
    {
        return new DateTime(date.Year, date.Month, 1);
    }
}

基本的FLEE设置(您必须根据需要进行自定义/调整)

ExpressionContext context = new ExpressionContext();
//Tell FLEE to expect a DateTime result; if the expression evaluates otherwise, 
//throws an ExpressionCompileException when compiling the expression
context.Options.ResultType = typeof(DateTime);
//Instruct FLEE to expose the `DateTime` static members and have 
//them accessible via "DateTime".
//This mimics the same exact C# syntax to access `DateTime.Now`
context.Imports.AddType(typeof(DateTime), "DateTime");
context.Imports.AddType(typeof(CustomFunctions));
//Expose your key variables like HireDate and EnrollmentDate
context.Variables["HireDate"] = GetHireDate(); //DateTime I suppose
context.Variables["EnrollmentDate"] = GetEnrollmentDate(); //DateTime I suppose
//Parse the expression, naturally the string would come from your data source
IGenericExpression<DateTime> expression = context.CompileGeneric<DateTime>(GetYourRule(), context);
DateTime date = expression.Evaluate();

那么你的规则可能看起来像:

string rule1 = "if(HireDate > EnrollmentDate, HireDate, EnrollmentDate)";
string rule2 = "HireDate.AddDays(90)";
string rule3 = "GetFirstDayOfMonth(EnrollmentDate.AddMonths(1))";
string rule4 = "GetFirstDayOfMonth(EnrollmentDate.AddMonths(if(EnrollmentDate.Day < 15, 1, 2)))"; 

下面的链接包含您想要的内容。本质上,它是一个可插入的DSL,允许定义无限的日期计划和集合,然后将其传递给函数、相交、统一等

http://code.google.com/p/drules/