Windows Server 2016 传递日期与 Oracle 64 位客户端不同

Windows Server 2016 Passing Dates Differently to Oracle 64 bit Client

我有一个 MVC 应用程序托管在 Windows Server 2016 上,它使用 32 位 Oracle 客户端连接到数据库。我升级到64位客户端了。

问题是服务器现在将不同格式的日期传递给数据库。它使用 C# 传递它:

comm.Parameters.Add(DATABASE_PARAMETERS.FIELDS.START_DATE, OracleDbType.Date).Value = startDate; //startDate is a DateTime variable

当那个日期进入数据库时​​,它的格式是 'dd-MMM-yyyy'。由于切换到 64 位客户端,日期在数据库端显示为 'yy-mm-dd'。

所以服务器传递日期的格式已经改变,这会导致数据库出错。数据库包含数百个包,因此更改数据库代码以重新格式化所有日期不是一种选择,我需要弄清楚如何让服务器再次正确传递它们。它在我的桌面上使用完全相同的代码仍然可以正常工作,这只是部署到服务器后的一个问题。

我试过在控制面板的区域设置中更改短日期格式。我尝试更改注册表中的短日期值“HKEY_USERS -> .DEFAULT -> 控制面板 -> 国际”。

我试过将日期传递给格式化字符串而不是 DateTime。

我试过在 web 项目的 Global.asax.cs 中添加这段代码:

        protected void Application_BeginRequest(Object sender, EventArgs e)
        {
            System.Globalization.CultureInfo newCulture = (System.Globalization.CultureInfo)System.Threading.Thread.CurrentThread.CurrentCulture.Clone();
            newCulture.DateTimeFormat.ShortDatePattern = "dd-MMM-yyyy";
            newCulture.DateTimeFormat.DateSeparator = "-";
            System.Threading.Thread.CurrentThread.CurrentCulture = newCulture;
        }

没有任何效果。该日期仍在数据库中显示为 'yy-mm-dd'。

知道如何解决这个问题吗?

编辑:要添加更多内容,具体抛出的错误是:

ORA-01858: a non-numeric character was found where a numeric was expected

并且正在数据库中的一行上发生:

l_end_date := '1-jan-2099';

因此添加格式:

l_end_date := to_date('1jan2099', 'dd-mon-yyyy');

解决了该问题,但我无法对整个系统中的每个日期变量都执行此操作。

问题是为什么当我从我的桌面 运行 它执行时没有问题,但是当我从服务器 运行 它 运行 时会导致错误完全相同的代码和相同的 Oracle 客户端?为什么只有一次我切换到 64 位 Oracle?

我是说服务器以不同方式传递它,因为在 DB 包中,作为调试步骤,我尝试的第一件事是打印 In 参数的值。

当我打印从我的桌面传来的变量时,它的格式是 'dd-MMM-yyyy'。当我使用 32 位客户端时,它是一样的。但是当部署到服务器时它是 'yy-mm-dd'.

所以我认为它是服务器上的东西,因为它可以在我的桌面上使用 32 位 Oracle 客户端。

编辑 2,这是要求的更多代码。这是调用 DB 包的 C# 函数:

        public CustomObject GetList(string username, DateTime? startDate, DateTime? endDate)
        {
            conn.Open();
            using (comm = new OracleCommand("db.funtion", conn))
            {
                comm.CommandType = CommandType.StoredProcedure;
                comm.Parameters.Add(TASConstants.DATABASE_PARAMETERS.FIELDS.BATCH_OUTPUT_CURSOR, OracleDbType.RefCursor, ParameterDirection.Output);
                comm.Parameters.Add(TASConstants.DATABASE_PARAMETERS.FIELDS.USER_NAME, OracleDbType.Varchar2, TASConstants.DATABASE_PARAMETERS.LENGTHS.USER_NAME).Value = username;
                comm.Parameters.Add(TASConstants.DATABASE_PARAMETERS.FIELDS.START_DATE, OracleDbType.Date).Value = startDate;
                comm.Parameters.Add(TASConstants.DATABASE_PARAMETERS.FIELDS.END_DATE, OracleDbType.Date).Value = endDate;
                comm.Parameters.Add(TASConstants.DATABASE_PARAMETERS.FIELDS.MESSAGE_FLAG, OracleDbType.Varchar2, TASConstants.DATABASE_PARAMETERS.LENGTHS.MESSAGE_FLAG);
                comm.Parameters[TASConstants.DATABASE_PARAMETERS.FIELDS.MESSAGE_FLAG].Direction = ParameterDirection.Output;
                comm.Parameters.Add(TASConstants.DATABASE_PARAMETERS.FIELDS.MESSAGE_BODY, OracleDbType.Varchar2, TASConstants.DATABASE_PARAMETERS.LENGTHS.MESSAGE_BODY);
                comm.Parameters[TASConstants.DATABASE_PARAMETERS.FIELDS.MESSAGE_BODY].Direction = ParameterDirection.Output;

                OracleDataAdapter da = new OracleDataAdapter(comm);
                DataTable result = new DataTable();
                BatchList batchList = new BatchList();

                try
                {
                    da.Fill(result); //error throw here

数据库包

  Procedure function(p_ssp_rec_refcur  Out ssp_rec_refcur
                            ,p_username        In Varchar2
                            ,p_start_date      In Date
                            ,p_end_date        In Date
                            ,p_successful_flag Out Varchar2
                            ,p_message         Out Varchar2) As
    
    l_start_date Date := p_start_date;
    l_end_date   Date := p_end_date;
  Begin
    If Not ws_base.authenticate_fn(p_username, 8013, ws_base.c_update, p_message) Then
      Return;
    End If;If p_end_date Is Null Then
      l_end_date := '1-jan-2099'; --error thrown here
    End If;
  
    If p_start_date Is Null Then
      l_start_date := '1-jan-1901'; 
    End If;

所以是的,在 l_end_date 的行中添加日期格式可以解决问题,但管理层不会接受该解决方案,因为 1) 它适用于 32 位 Oracle 客户端,并且 2) 它适用于我们桌面上的 64 位客户端。相同的代码,安装相同的客户端。

这里的谜团是为什么会这样?

该问题似乎与应用程序传递日期的方式无关。问题似乎是应用程序使用的存储过程编写不正确(或至少很差),并且取决于将客户端的 nls_date_format 设置为特定值才能正常运行。这意味着相同的代码 运行 在不同的机器上(或在同一台机器上使用不同的客户端,具体取决于客户端选择如何设置 nls_date_format)将表现不同。显然,这不是应用程序所希望的 属性。

按正确性顺序解决问题的选项(我知道您现在可能仅限于一些更创可贴的方法)

修复存储过程以使用正确的日期文字或显式 to_date 转换。

如果过程使用日期文字或显式 to_date 调用,无论客户端设置如何,它都会工作

If p_end_date Is Null Then
  l_end_date := date '2099-01-01'; 
End If;
  
If p_start_date Is Null Then
  l_start_date := to_date( '1-jan-1901', 'dd-mon-yyyy' ); 
End If;

在登录时显式设置 nls_date_format

创建数据库连接后,可以设置nls_date_format

alter session set nls_date_format = dd-mon-yyyy

这至少可以确保应用程序的代码库正在设置它特别需要的环境,这样当代码库被移动到不同的机器时它会正常工作。

在客户端设置nls_date_format为环境变量

在客户端,您可以为nls_date_format设置一个环境变量。您仍然依赖于客户端设置,但至少这是一个机器级别的客户端设置。

在注册表中为特定的 Oracle 客户端安装设置 nls_date_format

您可以在注册表中为您正在使用的特定 Oracle 客户端安装设置 nls_date_format 更详细地介绍了如何执行此操作。但是,这意味着,如果您尝试在同一台机器上使用不同的 Oracle 客户端,您将再次遇到故障,并且将来您必须记住,您修改了注册表以使应用程序之前可以运行。

hklm\software\oracle\key_OracleHome