Как сделать запрос с передачей нескольких параметров?
Добавил ListBox на WinForm (там изменил SelectionMode=MultiSimple). Для выбора несколько услуг. Сейчас визуальный выбор несколько услуг выбирается, но при выборе он суммирует только 1 услугу. И еще в данный момент есть ошибка если не выбрана ни одна из услуг (System.NullReferenceException: "Ссылка на объект не указывает на экземпляр объекта"), но это видимо из-за исключения ToString. Но самое важное, как правильно сделать суммирование всех выбранных услуг + цена 1 комнаты?
Нужно правильно суммировать 2 услуги сразу, а не только одну.
// Запрос к таблице услуг
private void GetAdditionalServices()
{
Con.Open();
SqlCommand cmd = new SqlCommand("SELECT* FROM AdditionalServicesTbl", Con);
SqlDataReader rdr;
rdr = cmd.ExecuteReader();
DataTable dt = new DataTable();
dt.Columns.Add("ServicesNum", typeof(int));
dt.Load(rdr);
listBox1.ValueMember = "ServicesNum";
listBox1.DataSource = dt;
Con.Close();
}
// Получаем счет за (услугу-тут проблема) + счет за комнату
int Price = 0;
int PriceServices = 0;
private void fetchCostServices()
{
Con.Open();
string Query = "SELECT ServicesCost FROM AdditionalServicesTbl where ServicesNum=" + listBox1.SelectedValue.ToString() + "";
SqlCommand cmd = new SqlCommand(Query, Con);
DataTable dt = new DataTable();
SqlDataAdapter sda = new SqlDataAdapter(cmd);
sda.Fill(dt);
foreach (DataRow dr in dt.Rows)
{
PriceServices = CONVERT.ToInt32(dr["ServicesCost"].ToString());
}
Con.Close();
}
private void fetchCostRooms()
{
Con.Open();
string Query = "SELECT TypeCost FROM RoomTbl join TypeTbl on RType=TypeNum where RNum="
+ RoomCb.SelectedValue.ToString() + "";
SqlCommand cmd = new SqlCommand(Query, Con);
DataTable dt = new DataTable();
SqlDataAdapter sda = new SqlDataAdapter(cmd);
sda.Fill(dt);
foreach (DataRow dr in dt.Rows)
{
Price = CONVERT.ToInt32(dr["TypeCost"].ToString());
}
Con.Close();
}
// Кол-во дней прожживание в гостинице (цена 1 комнаты + цена 1 услуги = общая сумма (цена комнаты + цена всех выбранных услуг)
private void DurationTb_TextChanged(object sender, EventArgs e)
{
if (AmountTb.Text == "")
{
AmountTb.Text = " 0";
}
else
{
try
{
int Total = Price * CONVERT.ToInt32(DurationTb.Text) + PriceServices * CONVERT.ToInt32(DurationTb.Text);
AmountTb.Text = "" + Total;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
Ответы (2 шт):
Нужно сделать метод, который даст тебе список всех услуг, вида:
'Услуга1', 'Услуга2' и т.д.
Затем изменить этот запрос:
SELECT ServicesCost FROM AdditionalServicesTbl where ServicesNum=" + listBox1.SelectedValue.ToString() + "
на:
SELECT SUM(ServicesCost) FROM AdditionalServicesTbl where servicesNum IN (" + ServicesList + ")";
Итак, в листбоксе можно выбрать несколько значений. А может быть и ни одного не выбрано. Нужно это проверять в начале метода.
if (listBox1.SelectedItems.Count == 0)
return;
Далее, нужно получить из листбокса выбранные значения и вставить их в sql-запрос.
string format = "SELECT ServicesCost FROM AdditionalServicesTbl WHERE ServicesNum in ({0})";
string values = string.Join(",", listBox1.SelectedItems.OfType<DataRowView>().Select(row => row[0]));
string query = string.Format(format, values);
К листбоксу прибинден дататейбл, поэтому в коллекции SelectedItems находятся DataRowView. Извлекаем их и берём значение первой (она же единственная) колонки (с индексом 0).
Если уж вы решили использовать DataTable, то знайте, что SqlDataAdapter способен сам открыть соединение, создать внутри себя SqlCommand и выполнить запрос.
var dt = new DataTable();
var sda = new SqlDataAdapter(query, connectionString);
sda.Fill(dt);
После чего суммируем полученные из БД данные:
foreach (DataRow row in dt.Rows)
{
PriceServices += row.Field<int>("ServicesCost");
}
Обратите внимание на +=.
Полный код метода может выглядеть так:
private void FetchCostServices()
{
if (listBox1.SelectedItems.Count == 0)
return;
string format = "SELECT ServicesCost FROM AdditionalServicesTbl WHERE ServicesNum in ({0})";
string values = string.Join(",", listBox1.SelectedItems.OfType<DataRowView>().Select(row => row[0]));
string query = string.Format(format, values);
var dt = new DataTable();
var sda = new SqlDataAdapter(query, connectionString);
sda.Fill(dt);
foreach (DataRow row in dt.Rows)
{
PriceServices += row.Field<int>("ServicesCost");
}
}
Но если нужна только сумма, то её можно и нужно посчитать прямо в БД. Пример в своём ответе показал Vitaliy Zlobin.
Для получения одного значения из БД нет смысла использовать DataTable. Применим метод ExecuteScalar.
private void FetchCostServices()
{
if (listBox1.SelectedItems.Count == 0)
return;
string format = "SELECT SUM(ServicesCost) FROM AdditionalServicesTbl WHERE ServicesNum in ({0})";
string values = string.Join(",", listBox1.SelectedItems.OfType<DataRowView>().Select(row => row[0]));
string query = string.Format(format, values);
using (var con = new SqlConnection(connectionString))
{
con.Open();
using (var cmd = new SqlCommand(query, con))
{
PriceServices = (int)cmd.ExecuteScalar();
}
}
}
Обратите внимание на применение using - это автоматически освободит ресурсы. Соединение при этом закроется тоже само, вызывать Close не нужно.
Не делайте SqlConnection полем класса. Создавайте его прямо по месту.
Я не знаю точные типы ваших данных, поэтому замените при необходимости int на правильные типы в вызовах OfType, Field и при касте.
Правильнее было бы сделать, чтобы метод возвращал значение: private int FetchCostServices(), вместо использования поля класса.
Ещё хорошо бы использовать асинхронность, но это отдельная тема. Задайте новый вопрос, если есть желание вникнуть.