FormatException из SQLDataAdapter.Update, вызванное нулевым значением

В моей таблице «Пользователь» (SQL Server) есть столбец (int, null), ActiveWorkRequestId. Я пытаюсь обновить таблицу с помощью SqlDataAdapter. Это настраивается с помощью клона команды Update по умолчанию на основе определения строго типизированного набора данных XSD (Visual Studio 2010). Так выглядит команда

UPDATE [User] SET [Username] = @p1, [ActiveWorkRequestId] = @p2
 WHERE (([Id] = @p3) AND ([Username] = @p4) AND ((@p5 = 1 AND
 [ActiveWorkRequestId] IS NULL) OR ([ActiveWorkRequestId] = @p6)))

В этом примере таблица «Пользователь» имеет 3 столбца: идентификатор, имя пользователя и ActiveWorkRequestId. Параметры p2, p5 и p6 имеют связанный столбец ActiveWorkRequestId.

Параметры времени выполнения для команды обновления (из измененной строки) в этом примере:

@p1 - "myusername"
@p2 - [DBNull]
@p3 - 126
@p4 - "myusername"
@p5 - [DBNull]
@p6 - [DBNull]

Я получаю все обновленные строки (возвращается только одна), затем обновляю следующим образом. ActiveWorkRequestId в измененной строке имеет значение null (фактически, как и в исходной строке). «dt» — это заполненная таблица данных из моего набора данных.

// This part is inline for convenience, only done once in fact
// Some irrelevant code omitted
string query = String.Format("SELECT * FROM [{0}]", dt.TableName);
string sqlCon = ""; // connection string actually got from settings
SqlDataAdapter da = new SqlDataAdapter(query, sqlCon);
SqlCommandBuilder cb = new SqlCommandBuilder(da);
cb.QuotePrefix = "[";
cb.QuoteSuffix = "]";
da.UpdateCommand = CopyCommandObject(dt, cb.GetUpdateCommand());

// ... This part may execute more than once
DataRow[] foundRows = dt.Select("", "",
    DataViewRowState.Added | DataViewRowState.ModifiedCurrent);
da.Update(foundRows);   // Exception here

// ... defined elsewhere
private static SqlCommand CopyCommandObject(DataTable dt, SqlCommand cmd)
{
    SqlCommand r = new SqlCommand(cmd.CommandText);
    r.Connection = cmd.Connection;
    foreach (SqlParameter p in cmd.Parameters)
    {
        DataColumn dc = dt.Columns[p.SourceColumn];
        // Put this in since p.IsNullable always seems to be false
        bool nullable = dc.AllowDBNull ? true : false;
        SqlParameter newParam = new SqlParameter(
             p.ParameterName,
             p.SqlDbType,
             p.Size,
             p.Direction,
             nullable,
             p.Precision,
             p.Scale,
             p.SourceColumn,
             p.SourceVersion,
             p.Value);
        r.Parameters.Add(newParam);
    }
    return (r);
}

Когда я вызываю обновление для SqlDataAdapter, создается исключение FormatException:

Не удалось преобразовать значение параметра из String в Int32.

Внутреннее исключение:

Входная строка имела неверный формат.

Я не могу узнать, против какого столбца он возражает, но единственный столбец int (кроме PK) - это ActiveWorkRequestId. Таким образом, кажется, что метод Update обрабатывает нулевое значение параметра как пустую строку и пытается привести ее к int32, даже если столбец таблицы и параметр оба допускают значение NULL.

Почему нельзя просто установить для столбца значение null (тем более, что оно уже равно null в базе данных)? Могу ли я что-нибудь с этим сделать?

Возможно, я должен упомянуть, что проект был только что импортирован в VS2010 (.NET 4.0) из VS2003 (.NET 1.1), где все работало нормально. Вот почему SqlDataAdapter создается в коде, а не просто с использованием TableAdapter, созданного разработчиком XSD — разработчик .NET 1.1 не создавал адаптеры таблиц. (Я не хочу регенерировать все XSD, если это возможно).


person Jeremy Tinkler    schedule 23.04.2015    source источник
comment
Похоже, что-то пытается присвоить строку NULL или DbNull в качестве значения параметра вместо экземпляра DbNull.Value.   -  person Joel Coehoorn    schedule 23.04.2015
comment
Строка DataTable определенно содержит DbNull.Value, поэтому, если это неверно расширено для параметра запроса, не будет ли это ошибкой в ​​​​ADO.NET? В этом процессе не участвует пользовательский код. Если это происходит для всех, кто пытается обновить значение nullable int, дату/время и т. д. с помощью SqlDataAdapter, то почему в Интернете нет людей, жалующихся на это?   -  person Jeremy Tinkler    schedule 23.04.2015
comment
Похоже, это давняя проблема, которую Microsoft не собирается решать... см., например. эта ссылка. Я могу попробовать упомянутое там расширение VS, поскольку кажется невозможным использовать SqlDataAdapter для обновления, где задействованы нестроковые столбцы с нулевым значением.   -  person Jeremy Tinkler    schedule 28.04.2015


Ответы (1)


Вместо использования метода CopyCommandObject() используйте:

da.UpdateCommand = cb.GetUpdateCommand().Clone();

Этот способ проще и менее подвержен ошибкам.

person gbrewer    schedule 29.04.2015
comment
Код был перенесен из .NET 1.1, которая не поддерживала SqlCommand.Clone(), поэтому, по-видимому, поэтому это было сделано таким образом. Однако я подозреваю, что в новой версии Framework не все необходимые свойства копировались старым кодом. - person Jeremy Tinkler; 29.04.2015