mekenix Ответов: 2

Ошибка преобразования типа данных nvarchar в числовой


Во-первых,это мой первый раз, когда я задаю вопрос о проблеме, с которой я сталкиваюсь здесь, поэтому приношу извинения за любые проблемы, которые я вызываю.

есть небольшая проблема с этой строкой кода, скажем, я выставляю цену на продукт, скажем, его 15 баксов, с этим кодом я могу поставить 15 баксов без проблем на sql. Но если я решу написать 15,10, то программа пойдет "ошибка преобразования типа данных nvarchar в числовой".

Вот мой код

private void btnKaydet_Click(object sender, EventArgs e)
        {
            try
            {
                if(MessageBox.Show("Bu ürünü kaydetmek istediğinize emin misiniz?","Save Product", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                {
                    string bid = "";  string cid="";
                    cn.Open();
                    cm = new SqlCommand("Select id from tblBrand where brand like '" + cbbMarka.Text + "'", cn);
                    dr = cm.ExecuteReader();
                    dr.Read();
                    if (dr.HasRows)
                    {
                        bid = dr[0].ToString();
                    }
                    dr.Close();
                    cn.Close();


                    cn.Open();
                    cm = new SqlCommand("Select id from tblCategory where category like '" + cbbKategori.Text + "'", cn);
                    dr = cm.ExecuteReader();
                    dr.Read();
                    if (dr.HasRows)
                    {
                        cid = dr[0].ToString();
                    }
                    dr.Close();
                    cn.Close();


                    cn.Open();
                    cm = new SqlCommand("INSERT INTO tblProduct (pcode, pdesc, bid, cid, price) VALUES(@pcode, @pdesc, @bid, @cid, @price)", cn);
                    cm.Parameters.AddWithValue("@pcode",textÜkodu.Text);
                    cm.Parameters.AddWithValue("@pdesc", textAçklma.Text);
                    cm.Parameters.AddWithValue("@bid", bid);
                    cm.Parameters.AddWithValue("@cid", cid);
                    cm.Parameters.AddWithValue("@price", textFiyat.Text);
                    cm.ExecuteNonQuery();
                    cn.Close();
                    MessageBox.Show("Ürün Başarıyla Kaydedildi.");
                    Clear();
                    flist.ÜrünleriYükle();
                }
            }catch(Exception ex)
            {
                cn.Close();
                MessageBox.Show(ex.Message);
            }
        }


это кнопка для сохранения (не обращайте внимания на язык :D ). Может ли кто-нибудь показать мне, как я могу избавиться от этой маленькой проблемы?

Что я уже пробовал:

я попытался удалить @ из цены, но это не сработало, и поискал несколько советов в сети, но ничего не решило.

2 Ответов

Рейтинг:
5

Richard MacCutchan

И еще кое-что ...

dr = cm.ExecuteReader();
dr.Read();
if (dr.HasRows)
{
    bid = dr[0].ToString();
}

Вы звоните доктору.Прочитайте, прежде чем вы убедитесь, что ваша команда SQL вернула несколько строк. Если это не так, то ваша программа рухнет в этот момент. Так и должно быть:
dr = cm.ExecuteReader();
if (dr.HasRows)
{
    dr.Read();
    bid = dr[0].ToString();
}

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


mekenix

Я вижу. Спасибо Вам за большую помощь, сэр.

Рейтинг:
15

OriginalGriff

Две вещи здесь не так, одна связана с тем, что вы заметили, другая-нет.
Тот, который у вас не первый, потому что он очень, очень важен:

Никогда не объединяйте строки для построения команды SQL. Это оставляет вас широко открытыми для случайной или преднамеренной атаки SQL-инъекции, которая может уничтожить всю вашу базу данных. Вместо этого всегда используйте параметризованные запросы.

Когда вы объединяете строки, вы вызываете проблемы, потому что SQL получает такие команды, как:

SELECT * FROM MyTable WHERE StreetAddress = 'Baker's Wood'
Цитата, добавленная пользователем, завершает строку в том, что касается SQL, и вы получаете проблемы. Но могло быть и хуже. Если я приду и наберу вместо этого: "x';DROP TABLE MyTable;--", то SQL получит совсем другую команду:
SELECT * FROM MyTable WHERE StreetAddress = 'x';DROP TABLE MyTable;--'
Которые SQL видит как три отдельные команды:
SELECT * FROM MyTable WHERE StreetAddress = 'x';
Совершенно правильный выбор
DROP TABLE MyTable;
Вполне допустимая команда "удалить таблицу"
--'
А все остальное-это комментарии.
Так оно и происходит: выбирает любые совпадающие строки, удаляет таблицу из базы данных и игнорирует все остальное.

Поэтому всегда используйте параметризованные запросы! Или будьте готовы часто восстанавливать свою БД из резервной копии. Вы ведь регулярно делаете резервные копии, не так ли?

Поэтому, когда большая часть вашего кода использует конкатенацию, как это:
cm = new SqlCommand("Select id from tblBrand where brand like '" + cbbMarka.Text + "'", cn);
Это означает, что yo0u нужно исправить все ваше приложение в приоритетном порядке.

Проблема, которую вы заметили, также проста: вы не проверяете или проверяете пользовательский ввод - вы просто передаете его непосредственно в SQL, предполагая, что он правильный:
cm = new SqlCommand("INSERT INTO tblProduct (pcode, pdesc, bid, cid, price) VALUES(@pcode, @pdesc, @bid, @cid, @price)", cn);
...
cm.Parameters.AddWithValue("@price", textFiyat.Text);

Вы не можете предполагать, что пользователи получат это правильно - они печатают плохо много времени, поэтому часть вашей задачи состоит в проверке того, что они печатают.
И то, что кажется вам правильным на вашем компьютере, не означает, что это правильно на компьютере, на котором работает sQL. Вы, кажется, используете запятую в качестве десятичного разделителя, так что "1,5" - это "полтора". Это не универсально: в Европе он должен быть "1,5" вместо этого. Обычно это не является проблемой, поскольку часть процесса проверки включает преобразование числа (или даты) из локальной культуры в числовое (или DateTime) значение, которое не зависит от культуры: например, десятичное или двойное.
Поэтому в верхней части вашего метоида проверьте свои значения, пытаясь преобразовать их, и сообщайте об ошибках, если вы не можете этого сделать:
decimal price;
if (!decimal.TryParse(textFiyat.Text, out price))
   {
   ... report problem to user ...
   return;
   }
Затем вы можете передать десятичное значение SQL через параметр, и дальнейшее преобразование не требуется - и ваша проблема исчезнет:
cm = new SqlCommand("INSERT INTO tblProduct (pcode, pdesc, bid, cid, price) VALUES(@pcode, @pdesc, @bid, @cid, @price)", cn);
...
cm.Parameters.AddWithValue("@price", price);
Сделайте это для каждого числового или датового ввода, и все будет в порядке.


mekenix

Уэлп я этого не предвидел, это было похоже на пощечину :Д. Я знал,что мое кодирование было испорчено, но я не знал, что оно было настолько испорчено :D, но я пишу эту программу как тренинг для себя, так что вашего полезного ответа мне более чем достаточно. Спасибо Вам, добрый сэр, что нашли время помочь мне. Но мне все еще неясно, что такое exatcly-параметризованные запросы? Можете ли вы привести мне пример использования моего кода?

OriginalGriff

Последний бит вашего кода использует параметризованные запросы: SQL, где у вас есть "@price" и так далее.

Я предполагаю, что вы копируете код, не пытаясь понять его или как он работает?
Это плохая идея, очень плохая. На данный момент это просто ваша БД, которая находится под угрозой - copy'N ' paste неправильный код на нем может быть вашим жестким диском ... если вы не понимаете кусок кода, он может делать все, что ему нравится, и это включает в себя вымогателей!

mekenix

Да.Я новичок в программировании, и это проблема, которую я хочу решить в течение длительного времени, начиная с этого момента я попытаюсь изучить код, который я написал и буду писать.Но я все еще не вижу, как последний бит спасает меня от будущих проблем. Разве люди, которые делают атаки sql-инъекций, просто не изменят несколько строк кода и все равно не вызовут тот же эффект?(мне очень жаль, если я говорю высокомерно, я пытаюсь узнать от вас как можно больше :))

OriginalGriff

Нет, это не работает таким образом.
Когда вы объединяете строки, вы передаете значение как часть строки, поэтому то, что пользователь вводит, обрабатывается как часть команды - это то, что позволяет SQL-инъекции работать.
С параметрами этого не происходит - параметры используются как переменные, а не обрабатываются как команды. SQL-инъекция не может происходить с параметризованными запросами.

mekenix

О, теперь я вижу это ясно.Спасибо Вам, добрый сэр. Вы мне очень помогли.

OriginalGriff

Всегда пожалуйста!

mekenix

Здравствуйте еще раз, сэр, я пытался изменить свои строки sql с помощью параметризованных запросов.Поскольку у меня нет реального опыта в этом, я снова пришел за вашим руководством.

cm = new SqlCommand("Select id from tblCategory where category like '" + cbbKategori.Текст + "'", cn);

cm = new SqlCommand("Select id from tblBrand where brand like '" + cbbMarka.Текст + "'", cn);


cm = new SqlCommand("SELECT category from tblCategory", cn);

Как именно я могу изменить эти строки с помощью параметризованных запросов, не могли бы вы мне объяснить?

OriginalGriff

Посмотри на свой код.
У вас есть три запроса - два с конкатенацией строк и один с параметрами.
Насколько трудно вам понять из этого кода, что вам нужно сделать? Вы не можете просто включить код, который вы не понимаете - это путь к катастрофе ...

mekenix

Как новичок, я бы сказал, довольно трудно. :Д
Я пытался и до сих пор не могу понять, как это сделать.

cm = new SqlCommand("Select id from tblBrand where brand like @brand", cn);
см.параметры.AddWithValue ("@brand",cbbMarka.Текст);

я пробовал писать так, но это не сработало.Может быть, мне не стоит писать addwithvalue? я понятия не имею :D

Вероятно, я пытаюсь добавить значение, когда пытаюсь выбрать из sql

OriginalGriff

Вероятно, это связано с тем, что LIKE-это "совпадающее" выражение: если оно не содержит подстановочных знаков SQL, оно совпадает с equals и требует идентичных строк. "%"- это подстановочный знак SQL для "ноль или больше чего-либо", так что:
.. Например, " % " + @Brand + "%" ...
Будет соответствовать любой строке с содержимым переменной в ней.
Таким образом, если @Brand было передано значение "сахар", то оно будет соответствовать "сахарному" и "коричневому сахару", но не "подсластителю"

В этом есть смысл?

mekenix

О, я понимаю, но я использую это для извлечения всех доступных брендов из базы данных в combobox, поэтому, когда я пытаюсь обновить его и щелкнуть combobox, он показывает мне все эти бренды.

OriginalGriff

Если вы хотите все бренды, то вы не хотите, как!