Как сделать запрос и выбрать запись с помощью TableAdapter набора данных в Visual Basic - PullRequest
0 голосов
/ 24 января 2020

ОБНОВЛЕНИЕ:

Так что я смог добиться некоторого прогресса, я получил этот код для работы .... У меня были проблемы с вычислением условия FOUND, поэтому я заключил его в For L oop, Что вы все думаете?

Dim ControlRow = NewBenefitsDataSet.Tables("FO_HealthcateHighways_Control").Select("NGID = 'HCHRXMEDTIP'")

    For Each Row As DataRow In ControlRow
        Do
            'Check if Already Processed, if so skip to next record
            If Row("NGID") = "HCHRXMEDTIP" Then
                MessageBox.Show("FOUND: " + Row("NGID"))
                Exit Do
            Else
                MessageBox.Show("GROUP NOT FOUND: " + Row("NGID"))
                Return False
            End If
        Loop
    Next

Прежде всего, я люблю это сообщество, и я действительно изо всех сил пытался получить правильный синтаксис от моего перехода с Visual Foxpro на VB. Независимо от того, сколько я ищу, я вижу дюжину различных вариантов и примеров, но мне трудно найти магическую комбинацию c для того, что я делаю.

Так что очень просто, мой набор данных и адаптер уже настроен в моем Form.XSD и из моего Code.VB я хочу запросить запись, но не уверен, как правильно ее кодировать ....

Что я здесь не так делаю? Еще раз спасибо заранее.

    Dim dtControl As NewBenefitsDataSet.FO_HealthcateHighways_ControlDataTable
    Dim drControl As NewBenefitsDataSet.FO_HealthcateHighways_ControlRow
    Dim daControlTableAdapter As New NewBenefitsDataSetTableAdapters.FO_HealthcateHighways_ControlTableAdapter

    'Fill Control Table Dataset
    daControlTableAdapter.Fill(NewBenefitsDataSet.FO_HealthcateHighways_Control)

    'Try to Query the DataTable (Intelisense tells me Variable is used before assigning a value)
    Dim cThisGroup() As DataRow = dtControl.Select("NPID = HCHRXMEDTIP")

    'So then I tried this variation (tells me Select is not a member of the Adapter)
    cThisGroup = daControlTableAdapter.Select("NPID = HCHRXMEDTIP")

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

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

Ответы [ 2 ]

0 голосов
/ 25 января 2020

Да, вы немного сбиты с толку, но очень легко понять почему, и я хорошо помню, когда несколько лет назад находился в том же положении

DataSet - это набор объектов DataTable. Он также содержит и управляет DataRelations

. DataTable - это коллекция объектов DataRow и коллекция объектов DataColumn.

DataRow - это коллекция значений, строк, целых чисел, даты и т. Д. c. DataColumns служат для ограничения определенного значения в DataRow типом. Вы можете иметь DataRow и запрашивать его для данных, на которые ссылается какой-либо DataColumn

———-

Ничто из этого не имеет ничего общего с базой данных, такой как SQL Сервер или Доступ (или Foxpro;)). У баз данных есть таблицы, у таблиц есть строки, у строк есть ячейки, у ячеек есть значения, столбцы набраны и т. Д. c ... Таким образом, это выглядит как DataSet, а база данных - это одно и то же / похожее, но важно помнить, что они Вы просто смоделированы по одним и тем же понятиям и имеете схожие имена для битов, которые заставляют их работать

Таким образом, инфраструктура DataSet / DataTable / DataRow / DataColumn имитирует расположение баз данных, но вы должны быть абсолютно ясны в уме : они не базы данных. Вы можете создать DataSet с DataTables и DataRelations, загрузить таблицы с данными и сохранить / загрузить с диска, даже не глядя на базу данных, такую ​​как SQL Server. DataSet - это локальный кеш данных, набор объектов, целью которых является локальное хранение данных в вашем приложении и допускающее поведение, похожее на базу данных, не потому, что они пытаются быть базами данных, а потому, что поведение допускает разумное моделирование связанных данные, и, имитируя базу данных, это означает, что они создают подходящий локальный кеш частей данных вашей базы данных - вы можете иметь как набор данных, так и базу данных, и загрузить некоторую часть данных из базы данных в набор данных, отредактировать ее, отправить его обратно, добавить новые данные в ds, отправить это тоже ..

Это разумная идея разбить любое приложение на слои, имея слой, который имеет дело только с сохранением данных и их моделированием. DataSets / Tables / et c достигают этого, потому что у вас может быть набор таблиц с именами, которые даже не совпадают с БД, но информация о том, как отобразить mone на другое, - все это доступно для вашего человека. может иметь столбец FamilyName, который отображается на столбец фамилии в БД. Несколько лет спустя кто-то решает перейти на новый сервер БД, переименовать столбцы и т.д. c, вы измените отображение в наборе данных так, чтобы FamilyName теперь ссылался на столбец с именем LastName. Ваш код продолжает использовать FamilyName, и когда набор когда-то отображал его в столбце db Surname, он теперь использует LastName. Вот почему мы накладываем вещи; чтобы свести к минимуму изменения, которые должны быть только внутри этого слоя

––––

Теперь в структуре существует устройство, называемое DataAdapter - оно знает, как использовать DbCommand / DbConnection для заполнения DataTable из таблицы базы данных. Это абстракция над DataReader (который не знает о DataSets / Tables) ... если вы обращались к своей БД с помощью DataReader, вам пришлось бы вставлять данные в вашу DataTable самостоятельно в al oop, что очень утомительно. Думайте о DataReader как о самом низком из самых низких; немного похоже на желание написать 3D-игру и рисовать на мониторе самостоятельно, а не использовать что-то вроде OpenGL. Они имеют свое применение, но в основном это если вы хотите быстрый доступ к данным только для чтения и вы не хотите сохранять результат. Например, представьте, что вы генерируете CSV на лету из таблицы базы данных и немедленно записываете данные CSV в сетевое соединение. Вам не нужно кэшировать всю таблицу 1 Гб из БД и загружать серверную память, прежде чем создавать CSV и отправлять его; Вы делаете это построчно в нескольких килобайтах памяти.

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

Dim r = connection.ExecuteReader("SELECT * FROM Person")
While(r.HasRows)
    Dim dt = myDS.Tables("Person")
    Dim ro = dt.NewRow()
    ro("Name") = r.GetString(1)
    ro("NumberChildren") = r.GetInt(2)
    ro("BirthDate") = r.GetDateTime(3)
End While

Да. Я бы предпочел засунуть булавки в глаза, чем зарабатывать на жизнь. Я бы даже не отдал его младшему разработчику


Шаг вверх от самого низкого из минимума; DataAdapter сокращает выбор и обновление кода до нескольких строк, но он все еще работает очень обобщенно c:

Dim dt as New DataTable
Dim da as New DataAdapter("SELECT...","connstr")
da.Fill(dt) 'it'll autocreate columns etc

По generi c Я имею в виду, что ваши данные в таблице хранятся как базовый объект; даже не так сложно, как быть строкой. Вы называете это:

myDataTable.Rows(0).Item("Name")

Это объект. Это строка внутри объекта, но чтобы использовать ее как строку, вы должны ее привести. И вам нужно получить к нему доступ, используя строку «Имя». Intellisense не поможет вам, если вы опечатаете его; вы просто получите cra sh, сказав, что «Naem» не является членом этой таблицы данных ». Так что все это на самом деле очень низкий уровень, и мы получаем доступ к строкам, как к массивам, должны помнить, что имя находится в позиции 1, или использовать строку с именем столбца и приведением:

myPerson.Name = DirectCast(myDataTable.Rows(0).Item("Name") as String)
myPerson.BirthDate = DirectCast(myDataTable.Rows(0).Item(2) as DateTime)

Что за головная боль; это не совсем то, на что мы подписались с современным безопасным типом языка, таким как VB / C# et c, код уродлив как грех, и мы не получаем никакой помощи с кодом от VS; Intellisense не годится для предложения имен столбцов, если они в строках

Итак ... Супер, наборы данных и др. - это сложные кеши данных, но это похоже на шаг назад, когда все является объектом и доступ к нему осуществляется давая строку. Мы могли бы написать несколько шаблонов, расширив DataRow и сделав его нестандартным:

Class PersonDataRow Extends DataRow
  Property fullName() As String
    Get
      If this.Item("Person") IsNot Nothing Then
          Return DirectCast(this.Item("Name"), String)
      Else
          Return Nothing
      End If

    End Get

Тогда мы могли бы сказать:

myPerson.Name = myPersonDataRow.Name;

Или даже лучше; просто используйте PersonDataRow в качестве сущности в нашей программе, которая хранит людей и объединяет другой класс Person вместе. Мы могли бы потратить несколько дней на написание всей этой шаблонной таблицы для каждого проекта и никогда не иметь грязного кода в другом месте

Если бы над этим был слой, который написал бы всю эту скучную шаблонную информацию для нас ..

——————

Введите конструктор DataSet, который вы назвали XSD. Это визуальное устройство, встроенное в Visual Studio, которое может подключаться к базе данных и помогает вам создавать собственные DataTables со всеми этими образцами, которые уже сделаны, и создает вещи под названием TableAdapters (которые являются оболочкой для базового c DataAdapter) для pu sh данные туда и обратно между БД и набором данных.

Так вот кикер; эти DataTabkes, которые делает дизайнер DataSet, не являются базовыми c низкоуровневыми, как я обсуждал ранее; у них есть эти дополнительные свойства и функции, которые делают вещи на более высоком уровне управления данными. Каждый столбец в таблице имеет свойство, которое преобразует и возвращает значение из базовой строки. Вместо того, чтобы иметь DataTable, который является коллекцией DataRow, которая содержит множество объектов, которые вы должны преобразовать обратно в строки и даты и т. Д. c, у вас будет PersonDataTable, который имеет PersonDataRows и PersonDataRows имеют свойства, которые Intellisense может читать, например Имя и дата рождения. И все это написано с помощью нескольких щелчков мыши и выбора некоторых имен, типов данных и т. Д. c и того, что происходит, когда значения равны нулю. Тогда ваш код может go от:

Dim bd as DateTime = DirectCast(myDT.Rows(0).Item("birhdate"), DateTime) 'generic weak typing, note the typo!

до

Dim bd as DateTime = myPersonDt(0).Birthdate 'first row, get the name

С сильно типизированными наборами данных код подгоняется; есть свойство .Birthdate PersonDataRow, которое извлекает базовую дату из строки, приводит ее к вам и возвращает.

Если у вас строго типизированный набор данных, старайтесь никогда не переходить в слабо типизированный режим, ie не делайте этого:

Dim myDT = myDs.Tables("Person")

Поскольку вам придется привести его к PersonDataTable (это уже PersonDataTable, но он будет упакован в виде простого DataTable), чтобы максимально использовать его:

Dim myDT = DirectCast(myDs.Tables("Person"), PersonDataTable)

Начинать бесполезно, бесполезно, потому что ... вы догадались ... есть свойство строго типизированного DataSet, которое будет возвращать PersonDataTable как полностью правильный тип PersonDataTable:

Dim myDT = myDS.Person

Вам даже не нужно делать переменную temp ... обычно она будет чище и аккуратнее чтобы просто ссылаться на свойство DataSet напрямую:

ForEach x as PersonDataRow in myDS.Person

В том же духе, не обращайтесь к. Свойство Rows - возвращает коллекцию DataRow, а не коллекцию PersonDataRow. Каждый раз, когда ваш Intellisense сообщает вам, что свойство, к которому вы обращаетесь, возвращает простой DataTable, DataRow и c вместо этого ищут именованное свойство, которое возвращает строгий тип:

myDS.Tables(0).Columns("Name") 'no; Tables(0) returns a DataTable - we just dropped to base types
myDS.Person.Columns("Name") 'no; we started well, got a PersonDataTable type out vis the .Person property of the dataset, but then went and accessed Columns("Name") which returns a DataColumn so we're back in base type land
myDS.Person.NameColumn 'yes; we stayed with the named properties all the way

Я продолжаю возвращаться к то же самое и здесь - старайтесь изо всех сил избегать использования строго именованных свойств вашего строго типизированного набора данных. Нет смысла возвращаться к низкому уровню


. Я упоминал TableAdapters ранее. Это DataAdapter на стероидах. Они предназначены для передачи данных sh туда и обратно между базой данных и существующей или новой строго типизированной DataTable. Они набираются и соединяются с одним видом DataTable - PersonDataTable имеет соответствующий PersonTableAdapter.

Когда вы создаете TableAdapter, вы делаете это с помощью мастера в конструкторе DataSet, если вы не перетаскивали представление таблицы базы данных из окна источников данных в DataSet (в этом случае это делается с некоторыми допущения по умолчанию)

В мастере вы обычно вводите запрос на выборку, который выбирает столбцы или их подмножество из базы данных, и мастер создает локальный DataTable, представляющий то, что вы запросили. На данный момент ни одна база данных не вышла из базы данных, мастер просто посмотрел на ваш запрос SELECT id, name, age, testpassdate FROM person WHERE id = @id и сказал: «Хорошо, это guid, строка, int, дата, из таблицы с именем person, она ищет для идентификатора, который является guid, теперь у меня достаточно информации, чтобы создать DataTable с 4 свойствами этих типов, и заполнить коллекцию параметров для запроса базы данных, использующей guid. и поскольку выбран столбец первичного ключа, я также могу сгенерировать обновление и удаление запросов ... "

Таким образом, вы получаете PersonTableAdapter, который оборачивает внутренний DataAdapter, он использует PersonDataTable для операций заполнения и обновления, и вы можете использовать его в своем коде, например так:

'SELECT ... FROM Person WHERE id = @id
personTA.Fill(myDS.Person, SOME_GUID_HERE)

Это проявление запроса, который мы использовали для создания адаптера. Мы закончили работу мастера на основе этого запроса; он сделал несколько запросов для выбора и обновления и загрузил их в табличный адаптер. Вы можете увидеть их, отладив ваше приложение и проверив .SelectCommand, .UpdateCommand, .InsertCommand и т. Д. c

Так что, если вы хотите запрашивать людей из БД по имени? Это делается только по идентификатору.

Щелкните правой кнопкой мыши TableAdapter в DataSet, добавьте запрос SELECT whatever FROM person WHERE name =@name и скажите мастеру, что хотите назвать его FillByName. В коде do:

personTA.FillByName(myDS.Person, "John Smith")

В конце концов вы соберете все различные запросы в разных адаптерах таблиц, чтобы ваше приложение работало. Некоторые из моих табличных адаптеров имеют 20 или более запросов. Они выбирают те же данные из таблицы, но по разным критериям. Некоторые даже используют объединения, например FillChildrenByParentName:

SELECT child.* FROM person parent INNER JOIN person child ON child.ParentID = parent.ID WHERE parent.Name = @name

Поскольку мы выбираем только дочерние записи (child,*), у нас все еще есть набор столбцов из Person без дополнительной информации о родителе. Это по-прежнему действительный набор данных, который помещается в PersonDataTable, и это означает, что уровень данных может соответствовать принципу «пользователь должен иметь возможность получить список всех дочерних элементов, принадлежащих лицу по имени X»

Важно точка: TableAdapters имеет функции Вставка / Обновление / Удаление (если вы отметили методы GenerateDBDirect "в расширенных настройках мастера), но они не предназначены для непосредственного использования, если вы не удаляете / обновляете данные которую вы никогда не загружали. Функция, которая сохраняет данные в БД с помощью табличного адаптера, называется Update, но она

  1. Принимает DataRow, коллекцию DataRow или DataTable
  2. Looks в состоянии datarow (добавлено, изменено, удалено и т. д. c), чтобы выяснить, нужно ли запускать INSERT, UPDATE или DELETE
  3. Запускает соответствующие SQL

I wi sh они назвали это Save, но это Update - просто помните, что вы загружаете данные в таблицу данных с помощью Fill. Вы изменяете таблицу данных, добавляете к ней, удаляете из нее, и когда вы хотите сохранить изменения, вы myTableAdapter.Update(theDataTable)


Теперь, быстрая критика вашего кода ..

''Don't need this, but you'd benefit from renaming the DataTable to have a shorter name like NBDS.Control
Dim dtControl As NewBenefitsDataSet.FO_HealthcateHighways_ControlDataTable

''Don't need this either
Dim drControl As NewBenefitsDataSet.FO_HealthcateHighways_ControlRow

''Again, call your dataset Nbds perhaps, and call the TA ControlTableAdapter
Dim daControlTableAdapter As New NewBenefitsDataSetTableAdapters.FO_HealthcateHighways_ControlTableAdapter

''Caution.. this probably my fills the whole database into the dataset
''Don't do this; put parameters into the query to restrict the data that comes down from the db     
''ie add a query to the TA of SELECT * FROM control WHERE NPID = @npid daControlTableAdapter.Fill(NewBenefitsDataSet.FO_HealthcateHighways_Control)

''Then do this
daControlTableAdapter.FillByNpid(NewBenefitsDataSet.FO_HealthcateHighways_Control, "HCHRXMEDTIP" )

Затем у вас были такие попытки

'Try to Query the DataTable (Intelisense tells me Variable is used before assigning a value)

Dim cThisGroup() As DataRow = dtControl.Select("NPID = HCHRXMEDTIP")

Intelisense сказал об этом, потому что вы объявили dtControl как типизированную переменную, но не дали ей значение. Vb не очень хороший язык в этом отношении, потому что легко спутать между MyDatset.PersonDataTable, который является типом PersonDataTable в типе объекта MyDataSet, и MyDataSet.Person, который является экземпляром PersonDataTable с именем Person, внутри экземпляр типа MyDataSet с именем MyDataSet

Смущен? Это вина исключительно vb за то, что мы позволили нам создавать экземпляры объектов, в которых переменная имеет то же имя, что и тип, а затем ошибка windows формирует команды для создания новых экземпляров набора данных в форме с тем же именем, что и тип. Другие языки, такие как c#, не позволяют создавать экземпляр типа с тем же именем, что и тип

Золотое правило, переименовывайте все свои вещи в форму после добавления! Когда вы отбрасываете набор данных в форму, vb делает

Dim MyDataSet as New MyDataSet

Переименовывает имя набора данных в форме так, чтобы оно называлось myDS et c, так что vb делает за кадром

Dim myDS as new MyDataSet

Это означает, что вы никогда не будете путать между типом объекта и именем экземпляра объекта

Если вы это сделали, а затем написали следующее:

 Dim dtControl As NewBenefitsDataSet.FO_HealthcateHighways_ControlDataTable
= myNBDS.FO_HealthcateHighways_Control

Это бы сработало. На самом деле это работает:

Dim dtControl As NewBenefitsDataSet.FO_HealthcateHighways_ControlDataTable
= NewBenefitsDataSet.FO_HealthcateHighways_Control

Но это путает как * &! #, Потому что первый NewBenefitsDataSet - это ТИП, а второй NewBenefitsDataSet - это МОМЕНТ, и это две совершенно разные вещи. Всегда избегайте именования переменных точно такими же именами, как у их типа

'So then I tried this variation (tells me Select is not a member of the Adapter)
cThisGroup = daControlTableAdapter.Select("NPID = HCHRXMEDTIP")

Это правда, что у tableadapter нет функций с именем Select, если вы не скажете, что вы хотите одну, удалив предложенный «FillBy» в мастере и написав "Выбрать". Имя по умолчанию для операции заполнения, имеющей параметры, - fillBy. Я рекомендую вам всегда редактировать имя, чтобы оно включало то, чем оно заполняется, например, когда я вызывал мой FillByName. Я не рекомендую вам использовать select, потому что он не поможет вам поддерживать умственное различие между DataTable (кеш локальных данных), базой данных (хранилище удаленных данных) и ролью табличного адаптера ... и не будет помогите, потому что у вас будет подпрограмма select, которая заполняет предоставленную DataTable, поэтому она заполняется без выбора. Вызов вещи, которые не отражают то, что они делают, является еще одним быстрым путаницей


Примечание по выбору: Select - это функция DataTable, которая будет искать данные, кэшированные в DataTable. Это не попадает в базу данных. Вы можете реально использовать его только для поиска данных в БД, если вы загрузили всю таблицу БД в DataTable ... не делайте этого. Создайте свой табличный адаптер, чтобы получить новый запрос SELECT, который выбирает небольшую часть строк БД, назовите его fillByX и заполните только те строки, которые вам нужны

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

0 голосов
/ 24 января 2020

Без доступа к коду для ваших элементов управления / классов, на который вы ссылаетесь, пример будет обобщенным c. Я предполагаю, что ваш NewBenefitsDataSet.FO_HealthcateHighways_ControlDataTable имеет тип DataTable и так далее.

Private Const _dbConnection As String = "Server=10.1.1.1;database=MyDataBase;uid=myUserId;pwd=MyPassword;"

    Protected Sub TestFunction()

        ' my sql Connection info
        Dim connection As New SqlConnection(_dbConnection)
        Dim commandText As String = "SELECT * FROM [dbo].MyTableName"

        Dim adapter As New SqlDataAdapter(commandText, connection)

        ' declare out DataSet and fill it
        Dim ds As New DataSet()
        adapter.Fill(ds)

        ' null check
        If ds IsNot Nothing AndAlso ds.Tables.Count > 0 Then

            ' unless you're executing multiple queries in your commandText, you should only have one table returned
            Dim dt As DataTable = ds.Tables(0)

            ' null check
            If dt IsNot Nothing AndAlso dt.Rows.Count > 0 Then

                ' get your array of DataRow
                Dim applicableRows As DataRow() = dt.Select("ProductType = 'Book'")

                For Each row As DataRow In applicableRows

                    ' Now I'm looping through each row

                    ' Comparing a rows field to a variable
                    ' Converting the field to a string, comparing to 'WhatToCompareTo', and then I'm saying to ignore case and culture
                    If String.Equals(CType(row.Item("ColumnName"), String), "WhatToCompareTo", StringComparison.InvariantCultureIgnoreCase) Then
                        ' execute logic
                    End If

                Next

            End If

        End If

        ' dispose adapter after done
        adapter.Dispose()

    End Sub
...