EPPlus 在返回 ValueError 的 VLookup 函数中将一个范围乘以一个常量

EPPlus multiplying a range by a constant inside a VLookup function returning ValueError

我在 ASP.Net 应用程序中使用 EPPlus 库。 我想要做的是打开一个电子表格,将一些值输入到单元格中,然后读取另一个包含计算结果的单元格。电子表格本身是机密的,所以我不能提供很多细节。

为了让我的计算工作,我不得不修改 EPplus 的源代码,更改 ExcelAddressExpression.cs 文件中的 Compile 函数以忽略 ParentIsLookupFunction bool,如问题底部所示。

所以它能够评估术语 5*$f$7

我想知道的是在什么情况下将 CompileResult 保留为 ExcelAddress 有用,因此我不会 运行 进入任何不正确的计算或电子表格其他部分的错误。

为了参考,这里是我到达这里的步骤:

我的代码是这样的

        using (ExcelPackage p = new ExcelPackage(FilePath, true))
        {
            ExcelWorksheet ws = p.Workbook.Worksheets["Calculations"];
            ws.Cells["b7"].Value = 50;
            ws.Cells["f9"].Value = 500000;
            ws.Cells["j216"].Calculate();
            string result = ws.Cells["j216"].Value.ToString();
        }

单元格 J216 中的公式为

=VLOOKUP($B+$F1+$K-$F-1,Sheet2!$A:$T3,5*$F+2*$B+$B-5,FALSE)

我得到的结果是“#VALUE!”

我附上了一个日志文件,发现问题出在 VLookup 函数中

Worksheet: Calculations
Address: J216
OfficeOpenXml.FormulaParsing.Exceptions.ExcelErrorValueException: #VALUE!
at OfficeOpenXml.FormulaParsing.Excel.Functions.IntArgumentParser.Parse(Object obj)
at OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup.LookupArguments..ctor(IEnumerable`1 arguments, ArgumentParsers argumentParsers, ParsingContext context)
at OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup.VLookup.Execute(IEnumerable`1 arguments, ParsingContext context)
at OfficeOpenXml.FormulaParsing.ExpressionGraph.FunctionCompilers.LookupFunctionCompiler.Compile(IEnumerable`1 children, ParsingContext context)
at OfficeOpenXml.FormulaParsing.ExpressionGraph.FunctionExpression.Compile()

接下来我下载了 EPPlus 的源代码,并在执行时调试代码,最终发现问题在 Operator.cs

的第 165 行
l = l ?? new CompileResult(0, DataType.Integer);
r = r ?? new CompileResult(0, DataType.Integer);
if (l.DataType == DataType.Integer && r.DataType == DataType.Integer)
{
    return new CompileResult(l.ResultNumeric*r.ResultNumeric, DataType.Integer);
}
else if ((l.IsNumeric || l.IsNumericString || l.IsDateString || l.Result is ExcelDataProvider.IRangeInfo) &&
    (r.IsNumeric || r.IsNumericString || r.IsDateString || r.Result is ExcelDataProvider.IRangeInfo))
{
    return new CompileResult(l.ResultNumeric*r.ResultNumeric, DataType.Decimal);
}
return new CompileResult(eErrorType.Value); 

计算方程式5*$F$7时,第二个参数是DataType ExcelAddress,导致编译结果异常

根本原因在于 ExcelAddressExpression.cs 文件的编译函数中,ParentIsLookupFunction 布尔值控制单元格是被评估还是保留为地址。

    public override CompileResult Compile()
    {
        if (ParentIsLookupFunction)
        {
            return new CompileResult(ExpressionString, DataType.ExcelAddress);
        }
        else
        {
            return CompileRangeValues();
        }
    }

我已将我的代码版本修改为

    public override CompileResult Compile()
    {
        return CompileRangeValues();
    }

正如问题顶部所说,我想知道的是你为什么要 return ExcelAddress CompileResult,它显然是有原因的,我不想破坏一些我电子表格中的其他计算。

我可以确认至少对于这个计算它现在工作正常。

几年前写过EPPlus的公式引擎,最近没怎么搞这个项目。如果我没记错的话,在某些情况下您不想编译表达式中的 excel 地址,而是将其传递给执行函数。您是否尝试过 运行 单元测试?公式引擎经过数百次测试,测试结果可以提供一些指导。

在尝试了不同的电子表格后,我能够回答这个问题。

如果地址未像我之前的示例那样进行操作,则将值作为 ExcelAddress 返回很有用 5*$F$7 例如VLookup.

如果有运算符,您将需要 return 单元格的内容。 我修改了 EPPlus 代码,现在检查公式

中的任何分隔项的运算符
    public override CompileResult Compile()
    {
        if (ParentIsLookupFunction &&
           Operator == null && 
           (Prev == null || Prev.Operator == null))
        {
             return new CompileResult(ExpressionString, DataType.ExcelAddress);
        }
        else
        {
            return CompileRangeValues();
        }
    }