Использование DateTime в SqlParameter для хранимой процедуры, ошибка формата - PullRequest
22 голосов
/ 08 января 2009

Я пытаюсь вызвать хранимую процедуру (на сервере SQL 2005) из C #, .NET 2.0, используя DateTime в качестве значения SqlParameter. Тип SQL в хранимой процедуре - datetime.

Выполнение sproc из SQL Management Studio работает нормально. Но каждый раз, когда я звоню из C #, я получаю сообщение об ошибке в формате даты.

Когда я запускаю SQL Profiler для просмотра вызовов, я затем копирую и вставляю вызов exec, чтобы посмотреть, что происходит. Вот мои наблюдения и заметки о том, что я пытался:

1) Если я передам DateTime непосредственно как DateTime или преобразую в SqlDateTime, поле будет окружено парой одинарных кавычек, например

@Date_Of_Birth=N''1/8/2009 8:06:17 PM''

2) Если я передам DateTime в виде строки, я получу только одинарные кавычки

3) Использование SqlDateTime.ToSqlString() не приводит к строке даты и времени в формате UTC (даже после преобразования в универсальное время)

4) Использование DateTime.ToString() не приводит к строке даты и времени в формате UTC.

5) Ручная установка DbType для SqlParameter на DateTime не меняет вышеприведенные наблюдения.

Итак, мои вопросы: как же я могу заставить C # передавать правильно отформатированное время в SqlParameter? Конечно, это распространенный случай, почему так трудно начать работать? Я не могу преобразовать DateTime в строку, совместимую с SQL (например, '2009-01-08T08: 22: 45')

EDIT

RE: BFree, код для фактического выполнения sproc выглядит следующим образом:

using (SqlCommand sprocCommand = new SqlCommand(sprocName))
{
    sprocCommand.Connection = transaction.Connection;
    sprocCommand.Transaction = transaction;
    sprocCommand.CommandType = System.Data.CommandType.StoredProcedure;
    sprocCommand.Parameters.AddRange(parameters.ToArray());
    sprocCommand.ExecuteNonQuery();
}

Более подробно о том, что я пробовал:

parameters.Add(new SqlParameter("@Date_Of_Birth", DOB));

parameters.Add(new SqlParameter("@Date_Of_Birth", DOB.ToUniversalTime()));

parameters.Add(new SqlParameter("@Date_Of_Birth", 
    DOB.ToUniversalTime().ToString()));

SqlParameter param = new SqlParameter("@Date_Of_Birth", 
    System.Data.SqlDbType.DateTime);
param.Value = DOB.ToUniversalTime();
parameters.Add(param);

SqlParameter param = new SqlParameter("@Date_Of_Birth", 
    SqlDbType.DateTime);
param.Value = new SqlDateTime(DOB.ToUniversalTime());
parameters.Add(param);

parameters.Add(new SqlParameter("@Date_Of_Birth", 
    new SqlDateTime(DOB.ToUniversalTime()).ToSqlString()));

Дополнительная редакция

Тот, который я думал, скорее всего, сработает:

SqlParameter param = new SqlParameter("@Date_Of_Birth",  
    System.Data.SqlDbType.DateTime);
param.Value = DOB;

Результат этого значения в вызове exec, как видно в SQL Profiler

@Date_Of_Birth=''2009-01-08 15:08:21:813''

Если я изменю это на:

@Date_Of_Birth='2009-01-08T15:08:21'

Работает, но не анализирует пару одинарных кавычек и не будет правильно преобразовывать в DateTime с пробелом между датой и временем и миллисекундами в конце.

Обновление и успех

Я скопировал / вставил код выше после запроса снизу. Я обрезал вещи здесь и там, чтобы быть кратким. Оказывается, моя проблема была в коде, который я пропустил, и я уверен, что любой из вас мог бы его заметить в одно мгновение. Я завернул свои вызовы sproc внутри транзакции. Оказывается, я просто не делал transaction.Commit() !!!!! Мне стыдно это говорить, но вот оно у тебя.

Я до сих пор не знаю, что происходит с синтаксисом, который я получаю от профилировщика. Сотрудник наблюдал со своим собственным экземпляром профилировщика со своего компьютера, и он вернул правильный синтаксис. Наблюдение за тем же самым исполнением от моего профилировщика показало неправильный синтаксис. Он действовал как «красная сельдь», заставляя меня поверить, что была проблема с синтаксисом запроса вместо гораздо более простого и верного ответа, а именно, что мне нужно было совершить транзакцию!

Я пометил приведенный ниже ответ как правильный и добавил некоторые положительные голоса за других, потому что они, в конце концов, ответили на вопрос, даже если они не устранили мою конкретную проблему (ошибка мозга).

Ответы [ 4 ]

28 голосов
/ 08 января 2009

Как вы настраиваете SqlParameter? Вы должны установить SqlDbType свойство на SqlDbType.DateTime и затем передать DateTime непосредственно параметру (НЕ преобразовывать в строку, вы тогда напрашиваются на кучу проблем).

Вы должны быть в состоянии получить значение в БД. Если нет, вот очень простой пример того, как это сделать:

static void Main(string[] args)
{
    // Create the connection.
    using (SqlConnection connection = new SqlConnection(@"Data Source=..."))
    {
        // Open the connection.
        connection.Open();

        // Create the command.
        using (SqlCommand command = new SqlCommand("xsp_Test", connection))
        {
            // Set the command type.
            command.CommandType = System.Data.CommandType.StoredProcedure;

            // Add the parameter.
            SqlParameter parameter = command.Parameters.Add("@dt",
                System.Data.SqlDbType.DateTime);

            // Set the value.
            parameter.Value = DateTime.Now;

            // Make the call.
            command.ExecuteNonQuery();
        }
    }
}

Я думаю, что отчасти проблема заключается в том, что вы обеспокоены тем, что факт времени в UTC не передается на SQL Server. Для этого вам не следует этого делать, поскольку SQL Server не знает, что определенное время находится в определенной локали / часовом поясе.

Если вы хотите сохранить значение UTC, преобразуйте его в UTC, прежде чем передавать его на SQL Server (если только ваш сервер не имеет того же часового пояса, что и код клиента, генерирующий DateTime, и даже тогда это риск ИМО). SQL Server сохранит это значение, и когда вы получите его обратно, если вы хотите отобразить его по местному времени, вы должны будете сделать это самостоятельно (что легко сделает структура DateTime).

Все это, как говорится, если вы выполняете преобразование, а затем передаете преобразованную дату UTC (дату, которая получается путем вызова метода ToUniversalTime , а не путем преобразования в строку) в сохраненную процедура.

И когда вы вернете значение, вызовите метод ToLocalTime , чтобы получить время в местном часовом поясе.

3 голосов
/ 09 января 2009

Вот как я добавляю параметры:

sprocCommand.Parameters.Add(New SqlParameter("@Date_Of_Birth",Data.SqlDbType.DateTime))
sprocCommand.Parameters("@Date_Of_Birth").Value = DOB

Я предполагаю, что когда вы записываете DOB, кавычек нет.

Используете ли вы сторонний элемент управления для получения даты? У меня были проблемы с тем, как из некоторых из них генерируется текстовое значение.

Наконец, работает ли он, если вы введете атрибут .Value параметра без ссылки на DOB?

0 голосов
/ 09 января 2009

Если вы используете Microsoft.ApplicationBlocks.Data, это сделает вызов ваших спроков одной строкой

SqlHelper.ExecuteNonQuery(ConnectionString, "SprocName", DOB)

Да, и я думаю, что casperOne правильный ... если вы хотите обеспечить правильную дату и время для нескольких часовых поясов, просто конвертируйте значение в UTC, прежде чем отправлять значение в SQL Server

SqlHelper.ExecuteNonQuery(ConnectionString, "SprocName", DOB.ToUniversalTime())
0 голосов
/ 09 января 2009

Просто используйте:

 param.AddWithValue("@Date_Of_Birth",DOB);

Это позаботится обо всех ваших проблемах.

...