2016-02-03 3 views
1

У меня вопрос о значениях базы данных и о том, как определить идентификатор значения, которое пользователь изменил в какой-то момент.Поиск в базе данных из списка ComboBox

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

Предположим, например, вы выбрали «Компонента A» из поля со списком, я бы хотел, чтобы вся заполняемая текстовая информация была заполнена соответствующей информацией из этой строки в базе данных (Name = Company A, Address = 123 ABC St., и т. д.)

Я могу заполнить поле со списком прекрасно. Это только, однако при изменении индекса ComboBox, что происходит эта конкретная ошибка:

Необработанное исключение типа «System.Data.OleDb.OleDbException» произошло в System.Data.dll

Дополнительная информация: несоответствие типа данных в выражении критериев.

Вот соответствующий код:

Imports System.Data.OleDb 
Public Class CustomerContact 

    Dim cn As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|datadirectory|\CentralDatabase.accdb;") 
    Dim da As New OleDbDataAdapter() 
    Dim dt As New DataTable() 

    Private Sub CustomerContact_Load(sender As Object, e As EventArgs) Handles MyBase.Load 
     cn.Open() 
     da.SelectCommand = New OleDbCommand("select * from Customers", cn) 
     da.Fill(dt) 

     Dim r As DataRow 
     For Each r In dt.Rows 
      cboVendorName.Items.Add(r("Name").ToString) 
      cboVendorName.ValueMember = "ID" 
     Next 
     cn.Close() 
    End Sub 

    Private Sub cboVendorName_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cboVendorName.SelectedIndexChanged 
     cn.Open() 
     da.SelectCommand = New OleDbCommand("select * from Customers WHERE id='" & cboVendorName.SelectedValue & "'", cn) 
     da.Fill(dt) 
      Dim r As DataRow 
      For Each r In dt.Rows 
      txtNewName.Text = "Name" 
      txtAddress.Text = "Address" 
      Next 
      cn.Close() 
    End Sub 

Ошибка поймана на линии 24 настоящего Кодекса, на втором da.Fill (DT). Теперь, очевидно, из исключения я знаю, что я отправляю неправильный тип данных в OleDbCommand, к сожалению, я новичок, когда дело доходит до SQL-команд, таких как это. Также имейте в виду, что я не могу даже проверить второй цикл For, тот, который должен заполнить информацию Customer в текстовые поля (для удобства я копировал только первые два текстовых поля, из которых всего девять). Я думаю, что я мог бы использовать оператор If, чтобы определить, прочитана ли строка, и оттуда заполняют текстовые поля, но я буду прыгать с этим препятствием, когда смогу это достичь.

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

+1

Используйте параметры, чтобы избежать SQL Injection , и исправить вашу ошибку. – LarsTech

+0

Я бы предположил, что вам вообще не нужно запускать новый запрос. У этого DataTable уже есть все клиенты. – Plutonix

+0

Спасибо, Плутоникс, я имею в виду, что я вижу, что вы говорите, как исходный DataTable уже заполнен информацией, но затем как определить, какая строка клиента заполняется, в зависимости от того, какой индекс выпадающего списка выбрал пользователь? Мой текущий процесс мышления, как вы видите, заключается в том, чтобы запускать новый запрос с идентификатором выбранного значения из выпадающего списка. – TGautier

ответ

1

Поскольку у вас уже есть все данные из этой таблицы в DataTable, вам вообще не нужно запускать запрос. Установка в виде нагрузки (если необходимо):

' form level object: 
Private ignore As Boolean 
Private dtCust As New DataTable 
... 
Dim SQL As String = "SELECT Id, Name, Address, City FROM Customer" 
Using dbcon = GetACEConnection() 
    Using cmd As New OleDbCommand(SQL, dbcon) 

     dbcon.Open() 
     dtCust.Load(cmd.ExecuteReader) 
    End Using 
End Using 

' pk required for Rows.Find 
ignore = True 
dtCust.PrimaryKey = New DataColumn() {dtCust.Columns(0)} 
cboCust.DataSource = dtCust 
cboCust.DisplayMember = "Name" 
cboCust.ValueMember = "Id" 
ignore = False 

Флаг игнорировать позволит игнорировать первое изменение, которое возникает как результат DataSource быть установлен. Это будет срабатывать до того, как будут установлены элементы Display и Value.

Предварительные вопросы/изменения:

  • Соединения предназначены для создания, используется и утилизированы. Это немного меньше относится к Access, но все же хорошая практика. Вместо того, чтобы подключать строки везде, метод GetACEConnection создает их для меня. Код is in this answer.
  • В интересах экономики, а не DataAdapter просто заполнить таблицу, я использовал читателю
  • В Using операторы создают и распоряжаться Command объекта, а также. Как правило, если объект имеет метод Dispose, поместите его в блок Using.
  • Я изложил столбцы для SQL. Если вам не нужны все столбцы, не просите их всех. Указание их также позволяет мне контролировать порядок (порядок отображения в DGV, ссылочные столбцы по индексу - dr(1) = ... - между прочим).

Важно то, что вместо того, чтобы добавлять элементы в БКК, я использовал эту DataTable как DataSource для комбо. ValueMember ничего не делает без DataSource - вот основная проблема, с которой вы столкнулись. Не было DataSource, поэтому SelectedValue всегда было Nothing.

Тогда в случае SelectedValueChanged:

Private Sub cboCust_SelectedValueChanged(sender As Object, 
       e As EventArgs) Handles cboCust.SelectedValueChanged 

    ' ignore changes during form load: 
    If ignore Then Exit Sub 

    Dim custId = Convert.ToInt32(cboCust.SelectedValue) 
    Dim dr = dtCust.Rows.Find(custId) 

    Console.WriteLine(dr("Id")) 
    Console.WriteLine(dr("Name")) 
    Console.WriteLine(dr("Address")) 
End Sub 

Используя выбранное значение, я найти соответствующую строку в DataTable. Найти возвращает, что DataRow (или ничего), поэтому я могу получить доступ ко всей другой информации. Результат:

Готье
sdfsdfsdf

Другой альтернативой было бы:

Dim rows = dtCust.Select(String.Format("Id={0}", custId)) 

Это будет возвращать массив DataRow соответствия критериям. String.Format полезен, когда целевой столбец является текстом. Этот метод не требует определения PK выше:

Dim rows = dtCust.Select(String.Format("Name='{0}'", searchText)) 

Для получения дополнительной информации см:

+0

Спасибо, что нашли время, чтобы помочь мне с этим, хотя у меня есть пара вопросов. Сначала и более очевидный, я должен был бы преобразовать «Console.WriteLine (dr (« Name »)) в нечто вроде txtNewName.Text = dr (« Name »), правильно? Я даже не стал бы задавать этот вопрос, если бы не этот второй: я получаю сообщение об ошибке, которое GetACEConnection не объявлено. Я попытался использовать dbcon как OleDbConnection = GetACEConnection, но это не решает эту проблему? На удивление, я не могу найти документацию в Интернете об этом. Нужен ли мне другой импорт, отличный от OleDb? – TGautier

+0

Да, вы можете сделать 'txtNewName.Text = dr (" Name "). ToString' ToString требуется в разделе' Option Strict', так как элементы DataRow являются Object. первая точка пули объясняет «GetAceConnection» - это локальный метод для создания соединений, так что строка conn не летает повсюду. Для этого есть ссылка на код. – Plutonix

+0

О, извините за отсутствие этого, я реализовал метод, и он работает!Могу ли я также использовать модуль для этого кода, если это конкретное соединение с базой данных используется во всех формах? Кроме того, я получаю InvalidCastException, созданный при объявлении 'custId = Conver.ToInt .......' В нем говорится, что я не могу использовать DataRowView в IConvertible? Я должен был бы сделать обработчик для этого, так как программа продолжается и на самом деле работает по назначению после сообщения? В любом случае, спасибо за помощь еще раз за Плутоникс, это ответ на мою проблему! – TGautier

Смежные вопросы