【问题标题】:Using CSharpCodeProvider to allow user created functions使用 CSharpCodeProvider 允许用户创建函数
【发布时间】:2013-02-06 13:25:51
【问题描述】:

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

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

  1. 入职日期或注册日期中的较晚者
  2. 入职日期 + 90 天
  3. 注册日期后每月的第一天
  4. 如果注册日期在当月 15 日之前,则生效日期为下个月 1 日。如果是 15 日或之后,则为之后的 1 日。

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

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

我最初的反应是寻找一种 DSL,它允许我定义日期操作函数并将其集成到我的解决方案中。然而,我对合适 DSL 的搜索却一无所获。

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

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

【问题讨论】:

    标签: c# .net runtime


    【解决方案1】:

    在这里查看我最近的回答:Parsing "DateTime.Now"?

    基本上,您可以轻松地利用现有库(如 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)))"; 
    

    【讨论】:

    • FLEE 可以使用 Nullable 类型吗?可以说,如果雇用日期未知,我需要能够将 null 传递给 HireDate,而不是构建一个接受两个 DateTime 参数的表达式?
    • @Aheho 我认为是这样,但您最好避免使用它拥有的各种隐式运算符并使用它的.Value 属性。例如,如果HireDateDateTime? 类型,那么您可能需要if(HireDate.Value &gt; DateTime.Now),而不是简单地检查if(HireDate &gt; DateTime.Now)。您还可以完全避免可空值有一个单独的布尔标志暴露“HasHireDate”,如果那是false,那么HireDate可能是过去的默认值,或者只是new DateTime()
    【解决方案2】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-07-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多