I have a small requirement in VB.NET and that is to fill an array with values retrieved from a table. That is i have a table called "user_roles" and that is having columns called "role_id" and "role_name". In the load event of the form, i want to execute a procedure and populate all the items (role_id) from the table "user_roles" into an array.
Can anyone please help me on this requirement.
Regards,
George
I assume that you better use a generic list instead of an array. Correct me if i'm wrong.
If you have filled your table in your codebehind, you can add the rolw_id's by iterating through all rows.
Dim allRoleIDs As New List(Of Int32)
For Each row As DataRow In user_roles.Rows
allRoleIDs.Add(CInt(row("role_id)")))
Next
Consider that its better to use a Datareader here because of performance reasons.
When you are using a strong typed dataset and want to avoid the extra roundtrip after filling the Datatable to add the ID's to the list, you have to extend the auto-generated Dataset DataAdapter Class(f.e. called user_rolesTableAdapter).
Don't use the Dataset's designer.vb class for that, because it will be overridden on every Dataset change. Use its codebehind class(without designer.vb) and add first the same namespace from your auto-generated TableAdapter(f.e. DatasetNameTableAdapter where DatasetName is the name of your Dataset). Then add following class(replace correct commands,column-index,class name of the partial class):
Namespace DatasetNameTableAdapters
Partial Public Class user_rolesTableAdapter
Public Function getListOfUserRolesID() As System.Collections.Generic.List(Of System.Int32)
Dim list As New System.Collections.Generic.List(Of System.Int32)
Dim command As System.Data.SqlClient.SqlCommand = Me.CommandCollection(0)
Dim previousConnectionState As System.Data.ConnectionState = command.Connection.State
If ((command.Connection.State And System.Data.ConnectionState.Open) _
<> System.Data.ConnectionState.Open) Then
command.Connection.Open()
End If
Try
Using reader As System.Data.SqlClient.SqlDataReader = command.ExecuteReader
While reader.Read
list.Add(reader.GetInt32(0))
End While
End Using
Finally
If (previousConnectionState = System.Data.ConnectionState.Closed) Then
command.Connection.Close()
End If
End Try
Return list
End Function
End Class
End NameSpace
Now you can get the user_roles ID's as generic List directly from the Dataadapter without iterating twice(first on filling the datatable and second on addind the ID's to an List).
Of course you can also use this Datareader approach in Page.Load without using a Dataset.
Related
I'm working on a code that returns a query result like MySqlCommand, all working well but what I'm trying to do is insert the result inside a ComboBox. The way for achieve this is the following:
Form load event execute the GetAvailableCategories function
The function executed download all the values and insert it into a dictionary
Now the dictionary returned need an iteration for each Items to insert in the ComboBox
Practice example:
1,3. Event that fire the function
Private Sub Service_Load(sender As Object, e As EventArgs) Handles MyBase.Load
For Each categoria In Categories.GetAvailableCategories()
service_category.Items.Add(categoria)
Next
End Sub
GetAvailableCategories function
Dim dic As New Dictionary(Of Integer, String)
For Each row In table.Rows
dic.Add(row(0), row(1))
Next
Return dic
How you can see in the 1,3 points I call the function that return the result. What I want to do is insert the row(0) as value of the item and row(1) as Item name. But Actually I get this result in the ComboBox:
[1, Hair cut]
and also I can't access to a specific position of the current item in the iteration. Maybe the dictionary isn't a good choice for this operation?
Sorry if the question could be stupid, but it's a long time since I don't program in vb.net and now I need to brush up a bit.
UPDATE
I've understood that I can assign the value access to the .key of my dictionary, so the result that I want achieve is correct if I do:
cateogoria.key (return the id of record taken from the db)
categoria.value (is the item name that'll display in the ComboBox)
now the problem's that: How to assign the value of the current item without create any other new class? For example:
service_category.Items.Add(categoria.key, categoria.value)
But I can't do this, any idea?
A List as a DataSource sounds like what you are really after. Relying on relative indices in different arrays is sort of flaky. There is not a lot about what these are, but a class would keep the related info together:
Public Class Service
Public Property Name As String
Public Property Category As String
Public Property Id As Int32
End Class
This will keep the different bits of information together. Use them to store the info read from the db and use a List to store all of them:\
Private Services As New List(of Service)
...
For Each row In table.Rows
Dim s As New Service
s.Name = row(0).ToString() '???
s.Category =...
s.Id = ...
Services.Add(s) ' add this item to list
Next
Finally, bind the List to the CBO:
myCbo.DataSource = Services
myCbo.DisplayMember = "Name" ' what to show in cbo
myCbo.ValueMember = "Id" ' what to use for SelectedValue
I dont really know what you want to show or what the db fields read are, so I am guessing. But the larger point is that a Class will keep the different bits of info together better than an array. The List can be the DataSource so that you dont even have to populate the CBO directly. The List can also be Sorted, searched, Filtered and so forth with linq.
When the user picks something, myCbo.SelectedItem should be that item (though it will need to be cast), or you can use SelectedIndex to find it in the list:
thisOne = Services(myCbo.SelectedIndex)
It is also usually a good idea to override ToString in the item/service class. This will determine what shows when a DisplayMember mapping is not available. Without this, WindowsApp2.Service might show for your items:
Public Overrides ToString() As String
Return String.Format("{0} ({1})", Name, Price)
End Sub
This would show something like
Haircut ($12.30)
How can I store the results of a DataReader into an array, but still be able to reference them by column name? I essentially want to be able to clone the DataReader's content so that I can close the reader and still have access. I don't want to store the items in a DataTable like everyone suggests.
I've seen a lot of answers, but I couldn't really find any for what I wanted
The easiest way I've found to do this is by populating the array with dictionaries with Strings as keys and Objects as values, like so:
' Read data from database
Dim result As New ArrayList()
Dr = myCommand.ExecuteReader()
' Add each entry to array list
While Dr.Read()
' Insert each column into a dictionary
Dim dict As New Dictionary(Of String, Object)
For count As Integer = 0 To (Dr.FieldCount - 1)
dict.Add(Dr.GetName(count), Dr(count))
Next
' Add the dictionary to the ArrayList
result.Add(dict)
End While
Dr.Close()
So, now you could loop through result with a for loop like this:
For Each dat As Dictionary(Of String, Object) In result
Console.Write(dat("ColName"))
Next
Quite similar to how you would do it if it were just the DataReader:
While Dr.Read()
Console.Write(Dr("ColName"))
End While
This example is using the MySQL/NET driver, but the same method can be used with the other popular database connectors.
I would like to fill a datatable with results from a SQL select statment but using a transaction. The reason that I am using a transaction is because I have a list of names (as a datatable), and I want to iterate through the list of names and select the database rows where the name = the name on the list. There are 500,000 names in the database and I only want to retreive the relevant rows. I have the code for the procedure as I think it should look like (untested) BUT I dont know HOW to place the data into a datatable .... so Im missing something where I declare the datatable and the 'fill' of that table , could someone help with this ? Or suggest how else I can get the information out of the batabase without looking up each name individually.
Using connection As New SQLite.SQLiteConnection(R2WconectionString)
connection.Open()
Dim sqliteTran As SQLite.SQLiteTransaction = connection.BeginTransaction()
Try
oMainQueryR = "SELECT NameID, Address, Ocupation FROM Employees Where Name= :Name"
Dim cmdSQLite As SQLite.SQLiteCommand = connection.CreateCommand()
With cmdSQLite
.CommandType = CommandType.Text
.CommandText = oMainQueryR
.Parameters.Add(":Name", SqlDbType.VarChar)
End With
'Prevent duplicate selects by using a dictionary
Dim NameInalready As New Dictionary(Of String, String)
For Each row As DataRow In TheLIST.Rows
If NameInalready.ContainsKey(row.Item("Name")) Then
Else
NameInalready.Add(row.Item("Name"), "")
cmdSQLite.Parameters(":Name").Value = row.Item("Name")
cmdSQLite.ExecuteNonQuery()
End If
Next
sqliteTran.Commit()
Catch ex As Exception
End Try
End Using
First, you don't need a transaction because you aren't updating the database.
Second, depending on the possible number of Names in TheLIST, it might be worthwhile for you to change the name selector to IN (i.e. SELECT * FROM Employees WHERE Name IN ('name1', 'name2'). However, if you expect more than about 10, this is probably not worth trouble.
Finally, you need to create a new DataTable to hold the results. Then you need to create a DataAdapter passing cmdSqlLite as the constructor parameter. And finally, replace your ExecuteNonQuery with DataAdapter.Fill(DataTable).
For example (after Dim cmdSQLite):
Dim oDataTable As New DataTable("Employees")
Dim oAdapter As New SqliteDataAdapter(cmdSQLite)
and replacing the ExecuteNonQuery line with:
oAdapter.Fill(oDataTable)
I will qualify this code by saying it may need some tweaks. I only work with class objects and collections, so my preference would have actually been to load a collection of Employee class instances.
I would have done that by replacing ExecuteNonQuery with ExecuteReader and then the loading the read data into a new class instance. This type of approach resolves various issues with serializing the data across service boundaries (i.e. Xml for web services) and also lets you embed business logic, if needed, into the classes.
I'm trying to solve a problem regarding types of list. First of all I have a stored procedure in my DB which does a select of a single column and I try to proceed it in my app in VB. By making a method function I declared a DataTable that loads through the SqlCommand(with the CloseConnection behavior). After that I publicly declared a List(Of String) which needs to be populated with the rows/items from the stored procedure that is on the way. Below is my snippet of the code:
Dim dt As New DataTable()
Try
If conn.State = ConnectionState.Open Then
conn.Close()
Else
conn.Open()
Dim cmd = New SqlCommand("LoadCodes", conn)
cmd.CommandType = CommandType.StoredProcedure
dt.Load(cmd.ExecuteReader(CommandBehavior.CloseConnection))
Dim collection As New List(Of DataRow)
collection = dt.AsEnumerable.ToList
LPrefix = collection.Cast(Of String)()
End If
Catch ex As Exception
MsgBox(ex.Message + vbCritical)
End Try
It's LPrefix = collection.Cast(Of String)() where I get an exception error telling me that I can't really convert it. The old fashion way is to iterate with for/for each loop but that's not what I want for best use of performance especially if the list will have thousands of rows from a single column. So basically I want to insert those items from that DataTable to the List(Of String) without using For/For Each loop.
Running on Visual Studio 2010 Ultimate, .NET Framework 4.0.
You don't need your collection at all. Using LINQ, you can extract the first column directly out of your data table:
dt.Load(cmd.ExecuteReader(CommandBehavior.CloseConnection))
LPrefix = (From row In dt.AsEnumerable()
Select row.Field(Of String)(0)).ToList()
Of course, this might use a loop internally, but since you want to copy each value into a list of strings, you cannot do it without looping through the data rows.
Another alternative would be to use an IEnumerable(Of String) instead of a List(Of String):
dt.Load(cmd.ExecuteReader(CommandBehavior.CloseConnection))
Dim LPrefixNew As IEnumerable(Of String) = _
From row In dt.AsEnumerable()
Select row.Field(Of String)(0)
You can iterate through IEnumerable just as you would through a list, but evaluation is lazy: As long as you don't access the elements, the DataTable is not traversed. So, accessing this IEnumerable is like reading the elements directly from the DataTable, just in a more convenient way.
Another word of advice: You should not try to reason about performance until you have measured it. For example, your line collection = dt.AsEnumerable.ToList probably already loops through your entire DataTable and copies each DataRow reference into a List of DataRows; so, with this line, you already have the performance penalty that you are trying to avoid.
So, don't automatically assume that some For loop is always slower than some single statement. Measure it, then optimize.
Assuming your DataRow only has one column you just need to instruct ConvertAll to cast it:
LPrefix = collection.ConvertAll(Function(x) x[0].ToString)
Thanks to Binary Worrier for c#-2-vb translation!
This seems like a really basic thing that I'm doing, yet I'm tearing my hair out trying to make it work.
My situation is this: I have a project which contains a large number of lookup tables, and I have all of these lookup tables represented in a single typed DataSet, which contains TableAdapters for each lookup. I've designed an editor for these lookup tables, which should allow editing of one of these at a time. My front-end is written in VB and WinForms, the back-end is a SOAP web service; I can successfully pass the changes to the DataSet back to the web service, but can't find a way to use a TableAdapter to update the single table that has been changed.
What I'm trying to do is instantiate the appropriate TableAdapter for the updated DataTable by sending the name of the table back to the web service along with the DataSet, then referring to the TableAdapter with a dynamic name. The normal way to instantiate a TableAdapter is this:
Dim ta As New dsLookupsTableAdapters.tlkpMyTableTableAdapter
What I'd like to do is this, but of course it doesn't work:
strTableName = "tlkpMyTable"
Dim ta As New dsLookupsTableAdapters(strTableName & "TableAdapter")
Is there any way to achieve this, or am I taking the wrong approach altogether? My other alternative is to write separate code for each table, which I'd prefer to avoid!
You can use Activator to create an instance of your TableAdapter from its string name, just like you want:
object adapter = Activator.CreateInstance(Type.GetType("My.Namespace.MyDataSetTableAdapters." + myTable.Name + "TableAdapter"));
Then, because TableAdapters don't have a common interface, you should use reflection to call its Update method:
adapter.GetType().GetMethod("Update").Invoke(adapter, null);
http://msdn.microsoft.com/en-us/library/system.type.getmethod.aspx
This is from memory, but roughly close enough. You can also use GetProperty to get the connection property and set it as required.
Not sure I 100% understand, do you have a single DataTable in your DataSet, or one DataTable per lookup table?
Anyway, perhaps you could you this approach to filter by lookup table?
It's pretty easy to create types at runtime given the (string) type name.
Here's a self-contained VB class which illustrates one way to do it: use System.Activator.CreateInstance to create instances of types using a string representation of the type name. Then you can cast it to a DataAdapter base class and use it like any other DataAdapter.
Public Class dsLookupsTableAdapters
Public Function CreateInstance(ByVal strName As String) As Object
CreateInstance = Nothing
For Each a As System.Reflection.Assembly In System.AppDomain.CurrentDomain.GetAssemblies()
Try
Dim strAssemblyName As String() = a.FullName.Split(New Char() {","c})
Dim strNameTemp As String = strAssemblyName(0) & "." & strName
Dim instance As Object = System.Activator.CreateInstance(a.FullName, strNameTemp)
If instance IsNot Nothing Then
Dim handle As System.Runtime.Remoting.ObjectHandle
handle = CType(instance, System.Runtime.Remoting.ObjectHandle)
Dim o As Object = handle.Unwrap()
CreateInstance = o
Exit For
End If
Catch ex As System.Exception
Continue For ' ignore exception, means type isn't there
End Try
Next
End Function
Public Class tlkpMyTableTableAdapter
Inherits System.Data.Common.DataAdapter
End Class
Public Sub Test()
' define type name. note that, in this sample, tlkpMyTableTableAdapter is a nested
' class and dsLookupsTableAdapters is the containing class, hence the "+". If, however,
' dsLookupsTableAdapters is a namespace, replace the "+" with a "."
Dim typeName As String = "dsLookupsTableAdapters+tlkpMyTableTableAdapter"
Dim adapter As System.Data.Common.DataAdapter
Dim o As Object = CreateInstance(typeName)
adapter = CType(o, System.Data.Common.DataAdapter)
End Sub
End Class
If you are using VB.Net 2008, then use the tableadaptermanager (http://msdn.microsoft.com/en-us/library/bb384426.aspx). I think this would be much easier to code against :)
Wade