ODP.NET:Oracle.ManagedDataAccess.Client.OracleException (0x80004005):.NET 5核心应用程序上的 ORA-01841

问题描述

尝试使用带有 C# .NET 5 Web 应用程序的 Oracle.ManagedDataAcces.Client (ODP.NET) 执行选择。

截至 2021 年 2 月 6 日,Oracle.ManagedDataAcces.Client 版本是最新的 3.21.1。

错误

Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware1 执行请求时发生未处理的异常。 Oracle.ManagedDataAccess.Client.OracleException (0x80004005): ORA-01841: (full) year 必须介于 -4713 和 +9999 之间,而不是 0 在 OracleInternal.ServiceObjects.OracleConnectionImpl.VerifyExecution(Int32& cursorId,Boolean bThrowArrayBindRelatedErrors,sqlStatementType sqlStatementType,Int32 arrayBindCount,OracleException& exceptionForArrayBindDML,Boolean& hasMoreRowsInDB,Boolean bFirstIterationDone)

这是查询代码

   public async Task<int> GetMyCount(string userName,int THE_YEAR)
    {
        try
        {
            string TEST = "STACK_OVERFLOW_TEST";
            builder.Clear();

            builder.Append($@" SELECT COUNT(*) 
                FROM {configuration.SCHEMA}.soME_TABLE CA 
                INNER JOIN {configuration.SCHEMA}.soME_OTHER_TABLE CS 
                ON CS.ID=CA.ID ");

            if (viewNotAll)
            {
                builder.Append($" INNER JOIN {unitOfWork.oracleDbOptions.DBSchemaQP}.ANOTHER_TABLE UT ON CA.soME_FIELD = UT.soME_FIELD ");
            }

            builder.Append(@$" WHERE CA.DATE_TO_FILET BETWEEN TO_DATE(CONCAT('0101',:THE_YEAR),'DDMMYYYY') 
            AND TO_DATE(CONCAT('3112','DDMMYYYY') ");


            if (TEST == "NO")
                builder.Append(" AND CS.TEST_FIELD=0 ");
            else
                builder.Append(" AND CS.TEST_FIELD=:THE_TEST_FIELD ");


            int result = 0;

            using (var cmd = unitOfWork.connection.CreateCommand())
            {

                cmd.Parameters.Add("THE_YEAR",OracleDbType.Int16,4,THE_YEAR,ParameterDirection.Input);
                cmd.Parameters.Add("THE_TEST_FIELD",OracleDbType.Varchar2,20,userName,ParameterDirection.Input);

                cmd.CommandText = builder.ToString();
                using (var reader = await cmd.ExecuteReaderAsync())
                {
                    while (await reader.ReadAsync())
                    {
                        result = reader.Isdbnull(0) ? 0 : reader.GetInt32(0);
                    }
                }

                return result;
            }
        }
        catch (Exception)
        {
            throw;
        }
    }

在 Oracle sql Developer 或 PL/sql 中执行查询有效。同样来自visual studio server exporer作品。会弹出一个窗口,要求输入参数,输入 VARCHAR2,它会很好地执行查询

我什么都试过了。出于测试目的,将类型从 Int16、Int32、VARCHAR2 更改为各种长度,也基于 Oracle .NET type guide,它指出 Int16 用于长度为 4 的数量,在我的情况下是年份。 没什么。

我还尝试使用显示in this stackoverflow question 检索 sql 查询历史记录,发现显然从外部触发的查询不会被记录。我无法通过此查询找到它们。

它的唯一工作方式是直接连接查询中的值:

builder.Append(@$" WHERE CA.soME_DATE BETWEEN TO_DATE(CONCAT('0101','{THE_YEAR}'),'DDMMYYYY') ");

但是我想使用参数来避免 sql 注入。

我做错了什么?

解决方法

更好用

builder.Append(@$" WHERE CA.DATE_TO_FILET BETWEEN :aDate AND :bDate");

cmd.Parameters.Add("aDate",OracleDbType.Date,ParameterDirection.Input).Value = new DateTime(THE_YEAR,1,1);
cmd.Parameters.Add("bDate",12,31);

            
,

CONCAT 连接两个字符串,所以 THE_YEAR 应该是一个包含四个字符的字符串而不是一个整数。你试过吗

cmd.Parameters.Add("THE_YEAR",OracleDbType.Varchar2,4,THE_YEAR.ToString(),ParameterDirection.Input);