C# 在参数化SQL字符串中使用.Net DateTime的正确方法

C# 在参数化SQL字符串中使用.Net DateTime的正确方法,c#,.net,sql,oracle,datetime,C#,.net,Sql,Oracle,Datetime,关于在Sql语句中使用.Net DateTime有几个问题,但没有一个解决了我的问题 我正在使用以下代码查询Oracle数据库: private DataTable QueryByIdAndDate(string id, DateTime fdate, DateTime tdate) { string query = "SELECT * FROM table WHERE ID = :id AND DATE_TIME BETWEEN :from AND :to" DbCommand

关于在Sql语句中使用.Net DateTime有几个问题,但没有一个解决了我的问题

我正在使用以下代码查询Oracle数据库:

private DataTable QueryByIdAndDate(string id, DateTime fdate, DateTime tdate) {
    string query = "SELECT * FROM table WHERE ID = :id AND DATE_TIME BETWEEN :from AND :to"
    DbCommand cmd = db.CreateCommand();
    cmd.CommandType = CommandType.Text;
    cmd.CommandText = query;

    DbParameter fromDate = CreateDateParameter(cmd, fdate);
    fromDate.ParameterName = "from";
    DbParameter toDate = CreateDateParameter(cmd, tdate);
    toDate.ParameterName = "to"; 
    DbParameter idParam = CreateStringParameter(cmd, id);
    idParam.ParameterName = "id";

    cmd.Parameters.AddRange(new DbParameter[] { fromDate, toDate, idParam });
    return db.ExecuteQuery(cmd);
}

private DbParameter CreateDateParameter(DbCommand cmd, DateTime date) {
    DbParameter param = cmd.CreateParameter();
    param.DbType = DbType.DateTime;
    param.Direction = ParameterDirection.Input;
    param.Value = date;
    return param;
}
但它不能正常工作。这样尝试代码时:

DataTable result = QueryByIdAndDate("12345", DateTime.Now, DateTime.Now.AddDays(1));
它给出了以下错误: ORA-01847:月份的日期必须介于1和月份的最后一天之间


我假设这与日期时间的格式有关,但我不知道如何可靠地解决这个问题。

DateTime
值转换为格式化的日期字符串:

private DbParameter CreateDateParameter(DbCommand cmd, DateTime date) 
{
     DbParameter param = cmd.CreateParameter();
     param.DbType = DbType.DateTime;
     param.Direction = ParameterDirection.Input;
     param.Value = date.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
     return param;
} 

即使您发送的是字符串值,
DbType
仍设置为
DateTime
,因此该值应正确转换。

Oracle希望日期格式为'DD Mon YYYY',因此您基本上需要以这种方式格式化日期值并将其分配给参数值

编辑:我注意到ODP的最新版本似乎能更好地处理日期时间参数。例如,在日期/时间列上的WHERE子句上有一个约束时,这样做就可以了:

DateTime dt = new DateTime(2012, 5, 21);
cmd.Parameters.Add("some_date_param", dt);
使用最新版本的ODP以这种方式运行查询效果良好。但是我有很多代码,其中日期/时间值需要作为格式化字符串传递,以便Oracle接受它们。

(根据注释…)

在这种情况下,参数的顺序很重要。。。尽管你给了他们名字。我没想到会出现这种情况,这是一个有点坏的驱动程序的迹象,但将您的代码更改为:

cmd.Parameters.AddRange(new DbParameter[] { idParam, fromDate, toDate });
你应该把它修好。(顺便说一句,这不一定是构造参数的方式,但这在这里有些无关紧要。)


不要开始将日期/时间值指定为字符串。引入比您需要的更多的字符串转换是一个非常糟糕的主意。

对于那些可能错过它的人,请参阅by@JonSkeet下的@kprobst的评论:

如果您试图使用名称绑定而未在
命令
上设置
BindByName
属性,则参数值等的格式设置是否良好无关紧要


(整个Oracle provider API都是一堆垃圾)。

您不会碰巧在30天内运行它,是吗?不会。使用DateTime.Now和DateTime.Now.AddDays(1)显示的示例失败;您的驱动程序是否确实支持命名参数?如果它试图按位置使用它们,它将使用“id”值作为“to”部分,这可能会被破坏。你在用哪个司机?@JonSkeet哇,你猜对了。我使用的是Oracle.DataAccess,它必须按位置使用它们,因为我在将参数放入数组时更改了参数的顺序,现在它可以工作了。经过一个小时的胡闹,结果真令人讨厌。把这个作为答案,你就赢了,谢谢@user12345613:很高兴看到心理调试经常工作:)请注意,即使这确实是问题所在,您也可能希望使用不变区域性,并且您肯定希望将“hh”更改为“hh”。我强烈建议不要自己进行这种转换。没有任何东西可以通过@JonSkeet。同意,我直接跳到了格式问题,从未考虑过参数顺序。根据您的建议更新。是的,我甚至没有考虑数组的顺序(这很愚蠢),因为它们是命名参数。此外,我愿意听取任何关于正确构造参数的意见或建议。根据我的经验,oracle.net驱动程序中有很多地方是不正确的,包括参数顺序或需要更改SQL语句以消除空引用异常。@user12345613:我不能说这是我有很多经验的领域,但是如果您使用强类型命令参数集合,您可能会得到诸如
AddWithValue
之类的选项,这会使您的生活更轻松。@JonSkeet我必须检查一下。就目前而言,我很高兴这能起作用。ODP.Net中的OracleCommand对象有一个名为
BindByName
的属性,当设置为true时,可以消除这个问题。默认情况下,它设置为false,因为以前版本的驱动程序用于按位置绑定。