Need to Extend the Scope of a DataTable - vb.net

I'm converting old VB 7 code which used ODBC to connect to an SQL Anywhere DB to VB 2013 and an Access 2010 DB.
I declare several DataSets at the top of the module but when I get into procedures and functions, I have lost the scope of the DataTable.
I have the following declared:
Public con As New System.Data.OleDb.OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|\CLI_CRVM.accdb")
Public extractDA As New OleDbDataAdapter("SELECT * FROM [extract]", con)
Public extractCB = New OleDbCommandBuilder(extractDA)
Public extractDT As DataTable
Public extractDR As DataRow
Then, in a clicked event of a button, I call a procedure which loads the DataTable:
extractCB.quoteprefix = "["
extractCB.quotesuffix = "]"
extractDT = New DataTable
extractDA.Fill(extractDT)
When it returns to the clicked event code, it does a For Each loop:
For Each extractDR As System.Data.DataRow In extractDT.Rows
At this point, I can see values from the DataTable like this:
ls_plan_code = Trim(extractDR("plan_code"))
MsgBox("Plan Code: " & ls_plan_code)
But when I call a procedure or function where I need the values from the DataTable, they are no longer available. Ie. when this executes:
Sub accumulation(ByVal adec_premium As Decimal, ByVal ai_stage As Integer)
Dim ldec_mode As Decimal
ldec_mode = CDec(extractDR("pay_mode"))
End Sub
I get this error: "Object reference not set to an instance of an object."
I know a workaround is to pass the DataRow to the sub routine; however, there are several DataTables and many procedures and functions, some of which call other procedures and functions which rely on data from other DataTables. Additionally, some sub routines write values to an answer DataTable which then gets written to the Access DB.
So, while I know this may not be "proper" form, I've got to get this code up and running to test values and if there were a way - as it did in the VB 7 code - to get the scope of the DataTable to extend throughout the entire module, I think my problems will be solved.
Thanks in advance!

In the following line:
For Each extractDR As System.Data.DataRow In extractDT.Rows
By specifying extractDR As System.Data.DataRow, you are creating a new variable. This variable is being assigned the value instead of the variable with the same name in the higher scope.
Simply remove the As System.Data.DataRow:
For Each extractDR In extractDT.Rows

Related

How to edit a record in an access database - visual basic

I want to edit a specific record in an access database but I keep on getting errors
this is the database I want to edit:
Access database
these are flashcards that the user has created and stored in an access database. What I want is that the user is able to edit the difficulty so it appears more/less often
This is the module:
Module Module1
Public Function runSQL(ByVal query As String) As DataTable
Dim connection As New OleDb.OleDbConnection("provider=microsoft.ACE.OLEDB.12.0;Data Source=flashcard login.accdb") 'Establishes connection to database
Dim dt As New DataTable 'Stores database in table called dt
Dim dataadapter As OleDb.OleDbDataAdapter
connection.Open() 'Opens connection
dataadapter = New OleDb.OleDbDataAdapter(query, connection)
dt.Clear() 'Clears datatable
dataadapter.Fill(dt) 'Fills datatable
connection.Close()
Return dt
End Function
End Module
And here is the button that the user can press to edit the database:
Private Sub btnSubmit_Click(sender As Object, e As EventArgs) Handles btnSubmit.Click
Dim sql As String
sql = "UPDATE flashcards set difficulty = '" & TxtDifficulty.Text
runSQL(sql)
End Sub
The difficulty column in the database should be able to be edited by the user through the value they entered in txtDifficulty.text
Good to hear I found the problem with the apostrophe.
I am going to need a where statement but the problem I have is that the user can create as much flashcards as they want so how would I write the where statement?
An INSERT statement does not have a WHERE clause but an UPDATE does and is usually by a primary key.
Look at how I add a new record ignoring mHasException and specifically using parameters. In this case a List is used but with little effort an array of DataRow can be passed instead.
Here is an example for updating a record with a DataRow.
To get other code samples for ms-access see the following repository.
In closing, in the above repository I don't get into all possibilities yet there should be enough there to get you going. And when reviewing code I flip between Add for adding a parameter and AddWithValue while Add is the recommend way but both are shown to see differences. see also Add vs AddWithValue.

Issues to make DataSet Public

I'm familair with VBA/SQL programming in a excel environment. I'm recently started to get acquainted with vb.net but I'm strugling with a number of topics. One of these topics relates to public DataSet handling. Hope someone can help with this.
I defined a Module with the below VB code with the attempt to create a (public accesable) DataSet:
Imports System
Imports System.Data
Imports System.Data.SqlClient
Module Module1
Public ds As DataSet
Public Sub CRMds()
Dim con As SqlConnection
con = New SqlConnection("Server=MI5047LT\DELIMA01;Initial Catalog=CRM01;Integrated Security=SSPI")
Dim str As String = "SELECT * FROM CRM01Main"
Dim cmd As New SqlCommand(str, con)
Dim da As New SqlDataAdapter(cmd)
Dim ds As New DataSet()
da.Fill(ds, "CRM01Main")
con.Close()
con = Nothing
End Sub
End Module
Within the above module (code not show) I'm also verifying if the DataSet is correctly populated (returns a correct DataSet population).
When I'm Loading a Form(1) I'm calling this module with a verification step checking if DataSet is correctly populated:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
call Module1.........
varXX = ds.Tables("CRM01Main").Rows(0)("Contact_ID").ToString
MsgBox(varXX)
End Sub
("Contact_ID" is the SQL Column Name)
The error message appears when running this application:
System.NullReferenceException: 'Object reference not set to an
instance of an object
apperatly meaning no values are populated in this DataSet(?).
Note: When I run this procedure with a "single variable (e.g. Var="OK") this variable is visible in any form opened.
Main Objective is to populate a initial created DataSet (through Module1) over multiple forms.
I'm realize above query is most probably a very basic issue but unfortunately I'm strugling for a number of weeks to figure out the correct code or procedure. Any help would ne highly appreciated.
This has got nothing to do with anything being Public and everything to do with scope. If you already have this:
Public ds As DataSet
then what is this for:
Dim ds As New DataSet()
If you had a rubbish bin outside your house and then you put a rubbish bin in your bedroom and threw some rubbish in it, would you expect that rubbish to magically appear in the rubbish bin outside? I would expect not.
If you expect to get something from that ds member variable then you need to assign something to it, not to a different variable declared locally within a method that ceases to exist when that method completes.
You should do some reading on the difference between local variable and member variables (fields).
EDIT:
To be explicit, don't declare a new variable and assign to that but rather assign to the existing variable:
ds = New DataSet()
Dim in vb.net is used to Dimension (Declare) a local variable. The compiler will look for the closest scope first for a variable. If the compiler finds a local variable it stops looking and that is the variable it uses.
If an Object has a .Dispose method, it should be called. For example, connections in ADO.net may have unmanaged code that needs to be cleaned up. Classes may be doing that clean up in the .Dispose method. Setting an object to Nothing does not accomplish this clean up in a timely manner. Using...End Using blocks were created to handle this for you. Notice that 3 ADO.net objects are in a single Using block separated by commas. The resource will be closed and disposed even if there is an error.
Public ds As DataSet
Public Sub CRMds()
Using con As New SqlConnection("Server=MI5047LT\DELIMA01;Initial Catalog=CRM01;Integrated Security=SSPI"),
cmd As New SqlCommand("SELECT * FROM CRM01Main;", con),
da As New SqlDataAdapter(cmd)
ds = New DataSet()
da.Fill(ds, "CRM01Main")
End Using
End Sub

Fill Datatable using SQL 'select' WITHIN A TRANSACTION

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.

Returning a DataTable in WCF and .NET

Thanks William, that was the ticket. Had to assign the name property on both ends [DataTable.TableName].
On a side note here: There appears to be some school of thought (no offense Marc) that the following statement is always true:
"Everything in the world can and should be made into an object."
It is, simply, not always true. There are cases where you cannot cram your 'object' into any cookie-cutter or class no matter how you try to customize it. For me to objectize this beast, I'd have to create roughly 4,000 objects. I don't have time to do that, and yet this project must run as a service. Frankly I think the developers at MickeySoft need to get out more into the real world, and see for themselves that although theory is great it does not represent true-life challenges. I am all for working with objects for the obvious benefits, but there is a reality that universals do not exist. In my trade, even 'most of the time' rarely happens.
Before they push out a new technology set, and cease support of an old one, they need to make sure that the new technology has the same capabilities the old one had.
For the record: The people who believe the above statement to be true are also the same people who would refuse to take the project I'm currently working on.
Just the same -- thank you both for your time, efforts and opinions!
I'm trying to create a WCF function that will return a table to my console testing app. I am a total noob. The data is 2-dimensional and looks like this:
23 Letter
42 Another Letter
43 Document
...
Here's what I'm trying to do:
<ServiceContract()> _
Public Interface ILetterWriter
<OperationContract()> _
Function GetLetter(ByVal LetterID As String, ByVal StateID As String, ByVal CompID As String, ByVal tblVar As DataTable) As String
<OperationContract()> _
Function GetLetterNames(ByVal DepartmentType As Integer) As DataTable
End Interface
Public Function GetLetterNames(ByVal DepartmentType As Integer) As DataTable Implements ILetterWriter.GetLetterNames
Dim SQLCon As New SqlClient.SqlConnection
Dim SQLCmd As New SqlClient.SqlCommand
'Connect to the database
SQLCon.ConnectionString = "Data Source=VMSQL08-SRV1;Initial Catalog=DotNetDev;User ID=aiis_pgmr;Password=ag58102;"
SQLCon.Open()
'Grab the stored procedure, which returns the letter names
SQLCmd.CommandText = "sp_GetLetters"
SQLCmd.CommandType = CommandType.StoredProcedure
SQLCmd.Connection = SQLCon
'Pass the parameters
SQLCmd.Parameters.AddWithValue("#LetterType", DepartmentType)
'Execute the stored procedure, fill the datatable from a data adapter
GetLetterNames = New DataTable
GetLetterNames.Load(SQLCmd.ExecuteReader)
'Shut it down
SQLCmd.Dispose()
SQLCon.Close()
SQLCon.Dispose()
End Function
...Of course, it won't work. I just need to get the WCF to pass a basic table to my console application. The execute SQL seems to work just fine, I just can't get the data back to my application.
Any help would be greatly appreciated.
Thanks,
Jason
I agree with the other poster.
However, if you are returning a DataTable, you have to set the "Name" property of the DataTable if you want to return it from a WCF Service.

How do I update a single table of a DataSet using a TableAdapter, without hard-coding the table name?

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