Dataset with Datatable - vb.net

I am trying to check if my dictionary contains values in my dataset.datatable and if its quantities in the second column of the dataset are less than or greater than the quantities in my datatable. I tried using the SELECT method but it doesn’t seem to work, I get the error BC30469 reference to non-shared member requires object reference?
I was just trying to do a simple search in the table first to see if I can even do that..... apparently not. Thanks for the help!
Dim row As DataRow = DataSet.DataTable.Select("ColumnName1 = 'value3'")
If Not row Is Nothing Then
searchedValue = row.Item("ColumnName2")
End If

You could get a dictionary to compare with the one you already have like this (assuming your key is a string and the amount an Int32 and that your dataset contains only one table):
Dim myDBDict As Dictionary(Of String, Int32) =
From e In myDataSet.Tables(0).Rows.Cast(Of DataRow).ToDictionary(Of String, Int32)(
Function(e) e.Field(Of String)("MyIDColumn"),
Function(e) e.Field(Of Int32)("myAmountColumn"))

Related

How do I query a local datatable and return information to a datatable in VB.net

I am trying to pass a query and existing datatable into a function. The function will query the passed datatable using the passed query and return the result.
Unfortunately, I am unable to return any data. I have posted my code below. Can anyone help me fix it? I don't know what I am doing wrong.
Public Function ExecQueryTest(Query As String, DT As DataTable) As DataTable
Dim Result() As DataRow
'initialize the table to have the same number of columns of the table that is passed into the function
Dim LocalTable As DataTable = DT
'initialize counting variables
Dim x, y As Integer
'use the select command to run a query and store the results in an array
Result = DT.Select(Query)
'remove all items from the localtable after initial formatting
For x = 0 To LocalTable.Rows.Count - 1
LocalTable.Rows.RemoveAt(0)
Next
'for loop to iterate for the amount of rows stored in result
For x = 0 To Result.GetUpperBound(0)
'add each array row into the table
LocalTable.Rows.Add(Result(x))
Next
ExecQueryTest = LocalTable
End Function
If there is a better way to accomplish my goal, I don't mind starting from scratch. I just want to be able to handle dynamic tables, queries, and be able to return the information in a datatable format.
The problem is here:
Dim LocalTable As DataTable = DT
That code does not do what you think it does. DataTable is a reference type, which means assigning DT to the LocalTable variable only assigns a reference to the same object. No new table is created, and nothing is copied. Therefore, this later code also clears out the original table:
'remove all items from the localtable after initial formatting
For x = 0 To LocalTable.Rows.Count - 1
LocalTable.Rows.RemoveAt(0)
Next
Try this instead:
Public Function ExecQueryTest(Query As String, DT As DataTable) As DataTable
ExecQueryTest = New DataTable() 'create new DataTable object to hold results
For Each row As DataRow In DT.Select(Query)
ExecQueryTest.LoadDataRow(row.ItemArray, True)
Next
End Function
Though you may also need to clone each DataRow record.
You can clear a table with just
LocalTable.Clear()
instead of using that cycle, Also the results of your select can be directly converted to datatable using
LocalTable = Result.CopyToDataTable

How to iterate display values in a combobox

I have a combobox whose values (displayvalue) are formatted (from a database query):
John Doe (11111)
where 11111 is the userID. The UserID is the login name for their machine and I want to default the selected value to the login UserID.
Since combobox.findstring(UserID) only matches if the entry begins with that string, I need to iterate through the values to look for the substring of UserID in the entries.
I've searched around here, but solutions seems to land all around this specific example. I can't seem to find a method that returns the display value at a specific index. What am I missing?
EDIT:
This is how I am populating my combobox:
Private Sub PopulateDropdown(strSQl As String, objControl As ComboBox, strTextField As String, strDataField As String)
Dim objdatareader As New DataTable
objdatareader = DataAccess.GetDataTable(strSQl)
objControl.DataSource = objdatareader
objControl.DisplayMember = strTextField
objControl.ValueMember = strDataField
End Sub
This may actually help folks trying to find the ValueMember of a combobox as well.
I am populating my combobox from a datatable, so this solution may only be valid from that. I am not really sure why this works. I just stumbled upon it.
First, I started by doing a for each item in combobox.items. According to intelisense, there is no property of .value, .text, .DisplayMember, or anything related to that. I did notice that the return type on combobox.items is a DataRowView. I am not sure why that is, but I went with it. One of the members of DataRowView is Row. It turns out, each column from the DataTable is added to the Row collection in 'item's' DataRowView. Rows(0) is the first column, Row(1) is the second, etc. I was then able to look in the Row's full text to find my userid, and then select that row by using the FindExactString of the combobox. The below code works (I built the datatable manually in this example):
dim UserID As String="12345"
dim MyTable as New Datatable
MyTable.Columns.Add("Value", Type.GetType("System.String"))
MyTable.Columns.Add("Text", Type.GetType("System.String"))
MyTable.rows.add("1","Bob Smith(11223)"
MyTable.rows.add("2","George Brown(12345)"
cboAssignedID.datasource=MyTable
cboAssignedID.DisplayMember="Text"
cboAssignedID.ValueMember="Value"
For Each item In cboAssignedID.Items
If InStr(item.Row(1).ToString, UserID) > 0 Then
cboAssignedID.SelectedIndex = cboAssignedID.FindStringExact(item.Row(1).ToString)
End If
Next

Group Dictionary values of a particular key

I have a Dictionary of String and a list of type variable. i.e.
Dim search As Dictionary(Of String, List(Of TypeResult))
My query is if I get 3 key-value pairs inside the dictionary and I want to groupby the values of only a particular key (say key no. 3), then how to do the same.
By groupby I want to eliminate any duplicate values coming in the List of a particular key.
Any code snippets or suggestions would be great.
Thanks In Advance.
I want to eliminate any duplicate values coming in the List of a particular key
There are a few ways to do this, depending on how and when you add items to the list for a particular key and at what time you need them to be without duplicates.
You could do it like this, to ensure that there are never any duplicate values in the list.
Sub AddItem(ByVal key As String, ByVal val as TypeResult)
Dim values As List(Of TypeResult)
If (search.TryGetValue(key, values)) Then
If (Not values.Contains(val)) Then
values.Add(val)
End If
Else
values = new List(Of TypeResult)()
values.Add(val)
search.Add(key, values)
End If
End Sub
Note that TypeResult must provide an Equals and GetHashCode implementation, otherwise the call to Contains may not provide you with the desired behavior.
Another option is to filter out duplicate values when they are being returned to someone asking for the values of a specific key.
Function DistinctValuesFor(ByVal key As String) As IEnumerable(Of TypeResult)
Dim values As List(Of TypeResult)
If (search.TryGetValue(key, values)) Then
DistinctValuesFor = values.Distinct()
Else
DistinctValuesFor = Enumerable.Empty(Of TypeResult)()
End If
End Function

Checking duplicate Values on DataGrid

I have a DataGrid which is bound with a DataTable having two columns which store sequences, in my DataGrid these sequence columns are bound with DataGridViewComboBoxes. User is able to set sequence from ComboBoxes. Default values in sequence columns is 0.
I just want to check duplicacy in both the columns on button click, user should not be able select any duplicate value in both the columns.
If i implement it by using ToTable method of DataView to find distinct values it also takes rows with value "0"
if i implement unique constraint on column on DataTable it also checks for 0.
If try to remove values with 0 it also changes DataGrid As DataGrid is bound with DataTable
If i try to declare a new DataTable from existing dataTable it also gets bound to DataGrid Automatically.
Please help me.
Here is an example of how you can check for duplicate values in a DataTable:
Option Strict On
Module Module1
Sub Main()
Dim dt As New DataTable
dt.Columns.Add("mycolumn", GetType(Integer))
dt.Rows.Add({"1"})
dt.Rows.Add({"2"})
dt.Rows.Add({"2"})
dt.Rows.Add({"4"})
dt.Rows.Add({"7"})
Dim duplicateDictionary As New Dictionary(Of Integer, Integer) 'value, count
For Each row As DataRow In dt.Rows
Dim count As Integer = 0
Dim value As Integer = CInt(row("mycolumn"))
duplicateDictionary.TryGetValue(value, count)
duplicateDictionary(value) = count + 1
Next
For Each kv As KeyValuePair(Of Integer, Integer) In duplicateDictionary
If kv.Value > 1 Then 'we have a duplicate
Debug.WriteLine("{0} is a duplicated value, encountered {1} times", kv.Key, kv.Value)
End If
Next
End Sub
End Module
Adding a UniqueConstraint is possible as well, but I find it too intrusive at times, depending on how your editing works. For direct in-grid editing, you may want the user to save a non-valid record in memory, and allow to fix the error, showing validation errors instead of constraint violation exception. Of course you never save invalid data to the database.

Simplest/fastest way to check if value exists in DataTable in VB.net?

I have a DataTable (currently with multiple columns but I could just grab one column if it makes it easier). I want to check if a String value exists in a column of the DataTable. (I'm doing it many times so I want it to be reasonably fast.)
What is a good way to do this? Iterating through the DataTable rows each time seems like a bad way. Can I convert the column to a flat List/Array format, and use a built in function? Something like myStrList.Contains("value")?
You can use select to find whether that value exist or not. If so, it returns rows or it will not. Here is some sample code to help you.
Dim foundRow() As DataRow
foundRow = dt.Select("SalesCategory='HP'")
If the data in your DataTable doesn't change very often, and you search the DataTable multiple times, and your DataTable contains many rows, then it's likely going to be a lot faster to build your own index for the data.
The simplest way to do this is to sort the data by the key column so that you can then do a binary search on the sorted list. For instance, you can build an index like this:
Private Function BuildIndex(table As DataTable, keyColumnIndex As Integer) As List(Of String)
Dim index As New List(Of String)(table.Rows.Count)
For Each row As DataRow in table.Rows
index.Add(row(keyColumnIndex))
Next
index.Sort()
Return index
End Function
Then, you can check if a value exists in the index quickly with a binary search, like this:
Private Function ItemExists(index As List(Of String), key As String) As Boolean
Dim index As Integer = index.BinarySearch(key)
If index >= 0 Then
Return True
Else
Return False
End If
End Function
You could also do the same thing with a simple string array. Or, you could use a Dictionary object (which is an implementation of a hash table) to build a hash index of your DataTable, for instance:
Private Function BuildIndex(table As DataTable, keyColumnIndex As Integer) As Dictionary(Of String, DataRow)
Dim index As New Dictionary(Of String, DataRow)(table.Rows.Count)
For Each row As DataRow in table.Rows
index(row(keyColumnIndex)) = row
Next
Return index
End Function
Then, you can get the matching DataRow for a given key, like this:
Dim index As Dictionary(Of String, DataRow) = BuildIndex(myDataTable, myKeyColumnIndex)
Dim row As DataRow = Nothing
If index.TryGetValue(myKey, row) Then
' row was found, can now use row variable to access all the data in that row
Else
' row with that key does not exist
End If
You may also want to look into using either the SortedList or SortedDictionary class. Both of these are implementations of binary trees. It's hard to say which of all of these options is going to be fastest in your particular scenario. It all depends on the type of data, how often the index needs to be re-built, how often you search it, how many rows are in the DataTable, and what you need to do with the found items. The best thing to do would be to try each one in a test case and see which one works best for what you need.
You should use row filter or DataTable.Rows.Find() instead of select (select does not use indexes). Depending on your table structure, specifically if your field in question is indexed (locally), performance of either way should be much faster than looping through all rows. In .NET, a set of fields needs to be a PrimaryKey to become indexed.
If your field is not indexed, I would avoid both select and row filter, because aside from overhead of class complexity, they don't offer compile time check for correctness of your condition. If it's a long one, you may end up spending lots of time debugging it once in a while.
It is always preferable to have your check strictly typed. Having first defined an underlying type, you can also define this helper method, which you can convert to extension method of DataTable class later:
Shared Function CheckValue(myTable As DataTable, columnName As String, searchValue As String) As Boolean
For row As DataRow In myTable.Rows
If row(columnName) = searchValue Then Return True
Next
Return False
End Function
or a more generic version of it:
Shared Function CheckValue(myTable As DataTable, checkFunc As Func(Of DataRow, Boolean)) As Boolean
For Each row As DataRow In myTable.Rows
If checkFunc(row) Then Return True
Next
Return False
End Function
and its usage:
CheckValue(myTable, Function(x) x("myColumn") = "123")
If your row class has MyColumn property of type String, it becomes:
CheckValue(myTable, Function(x) x.myColumn = "123")
One of the benefits of above approach is that you are able to feed calculated fields into your check condition, since myColumn here does not need to match a physical myColumn in the table/database.
bool exists = dt.AsEnumerable().Where(c => c.Field<string>("Author").Equals("your lookup value")).Count() > 0;