I'm dusting off vb.net needs to work and I have a question that I hope you can clarify. My application require a connection with a database, now when the form is load I execute this function:
Sub OpenConnection()
Try
Dim connection As New Connection
Dim MysqlConn = connection.establishConnection()
MysqlConn.Open()
Catch myerror As MySqlException
MessageBox.Show("Connection failed: " & myerror.Message)
Finally
MysqlConn.Dispose()
End Try
End Sub
The problem's that connection and MysqlConn variable is used also in another function so I've declared this two variable as global. How you can see this variable are re-declared in the OpenConnection() 'cause I've a form where the user can change the settings connection string. Now if the software can't connect to the database for a wrong credentials the user change this into the setting form but if I remove connection and MysqlConn the application use the past instance of a class so even if the credentials are correct, not declaring a new instance of the connection the application can't connect. For the moment I've solved with a re-declaration inside the OpenConnection() but is this a good move? Is there a way to have the two global variables and invoke a new instance without redefine the function?
Connection class - EstablishConnection function
Public Function establishConnection()
Dim MysqlConn As MySqlConnection = New MySqlConnection()
Dim server_name = My.Settings.server_name
Dim username = My.Settings.username
Dim password = My.Settings.password
Dim database = My.Settings.database
MysqlConn.ConnectionString = "server=" & server_name & ";" _
& "user id=" & username & ";" _
& "password=" & password & ";" _
& "database=" & database & ";"
Return MysqlConn
End Function
How to redeclare an instance of a class?
Variables are declared.
Objects are instanced (instantiated).
They are never redeclared; another new one is just created. Take this line (similar to yours):
Dim connection As MySQLConnection
It declares a variable, giving it a name and type.
Dim connection As MySQLConnection = New MySQLConnection
The second part creates an instance - the New operator is the key: it creates a new instance. The short hand form you used does both at once and perhaps makes it unclear that 2 different operations - declaring a variable and creating an instance - are going on.
Apparently, you have a class named Connection to "manage" connections. But the top method, OpenConnection is just using it to set/reset some global object var. dbConnections should generally be created, used and disposed of.
A connection maker-helper like you have can prevent having connection strings
sprinkled all over the code. Eg:
Function GetMySQLConnection(Optional usr As String = "root",
Optional pw As String = "123456") As MySqlConnection
Dim conStr As String
conStr = String.Format("Server=localhost;Port=3306;Database={0};Uid={1}; Pwd={2};",
MySQLDBase, usr, pw)
Return New MySqlConnection(constr)
End Function
This might be part of a small class to hold the DBname and other log in info. But using it doesnt require a global var:
Sub GetSomeData(...)
Using dbCon As MySqlConnection = GetMySQLConnection()
...
dbCon.Open()
myDA.Fill(myDT)
...
End Using
End Sub
At the start, a new connection object is created for this set of operations, which exists only locally (in that method). The Using block assures that the connection is closed (and returned to the pool) and disposed of when you are done with it.
Related
I am using a visual studio 2022 vb.net and mssql management studio.
login form
Private Sub Btnlogin_Click(sender As Object, e As EventArgs) Handles Btnlogin.Click
cn.Open()
cm = New SqlClient.SqlCommand("select * from table_user where username like '" & username.Text & "' and password like '" & password.Text & "'and usertype= '" & usertype.SelectedItem & "'", cn)
dr = cm.ExecuteReader
sda.Fill(dt)
If (dt.Rows.Count > 0) Then
MessageBox.Show("You are login as " + dt.Rows(0)(2))
If (usertype.SelectedIndex = 0) Then
Dim a As New dashboard
a.Show()
Me.Hide()
Else
Dim b As New Admin
b.Show()
Me.Hide()
End If
Else
MessageBox.Show("Username or Password Incorrect. CONTACT ADMINISTRATOR!")
End If
cn.Close()
End Sub
Module
Imports System.Data.SqlClient
Module Module1
Public cn As New SqlConnection("Data Source=DESKTOP-7POF5HE\SQLEXPRESS;Initial Catalog=dict;Integrated Security=True")
Public cm As New SqlCommand
Public dr As SqlDataReader
Public sda As SqlDataAdapter = New SqlDataAdapter(cm)
Public dt As DataTable = New DataTable()
End Module
CAN YOU HELP ME TO SOLVE THIS?
Seems there are several issues with this code.
In your module, you create the command object and the data adapter object. But in the form method, you create a new command object. But that will not update the data adapter to use that new command object. Class variables are reference types. They just point to an object in the memory somewhere. Your cm variable will point to the new command object, but your sda object will internally still point to the old command object.
Furthermore:
You are using both a data reader and a data adapter. Both have their pros and cons, but you probably don't need (or want) to use them both at the same time. I assume you want to use the data adapter. So you can drop the dr = cm.ExecuteReader line in the form method.
Since you will probably always want to create command objects and data adapter objects on the fly (as I would), you could remove them from the module. Just create them both as local variables in your form method.
Try to use the Using statement for such objects. They need to be disposed of nicely when the form method finishes. Otherwise they might keep valuable system resources in use until the .NET garbage collector disposes them (which will probably occur when you close your application, not earlier).
Also be careful with concatenating SQL statements from variables. What would happen here if you enter this text in your username textbox?: ';delete from table_user;--
Hint: Do not actually try it! It will try to delete all your users from the database table table_user. Just try to manually reproduce the SQL string that will be built in your form method. This is called an SQL injection attack. To avoid such nasty things, it's easiest to use SQL parameters in your SQL statements and pass them separately to your command object.
I'm using simple DataReader commands very often in my project.
To simplify it, I've created a function:
Public Function DataReaderFromCommand(ByRef uCn As SQLite.SQLiteConnection, ByVal uCommandText As String) As SQLite.SQLiteDataReader
Dim nCmdSel As SQLite.SQLiteCommand = uCn.CreateCommand
With nCmdSel
.CommandText = uCommandText
End With
Dim r As SQLite.SQLiteDataReader = nCmdSel.ExecuteReader
Return r
End Function
In my project I use it like this:
Using r As SQLite.SQLiteDataReader = DataReaderFromCommand(cnUser, "SELECT * FROM settings")
Do While r.Read
'do something
Loop
End Using'this should close the DataReader
In one case, I need to delete my database. However this fails with the error "File is locked by another process".
I tried to isolate the problem, and the locking occurs because of the function "DataReaderFromCommand".
Does anybody see what I'm doing wrong / what keeps the database locked?
I thought that after "End Using" of the datareader, the SQLiteCommand would also be disposed, so there are no further reference to the database.
You should probably be trying to do it this way:
Public Sub UsingDataReader(ByVal connectionString As String, ByVal commandText As String, ByVal action As Action(Of SQLite.SQLiteDataReader))
Using connection As New SQLite.SQLiteConnection(connectionString)
Using command As New SQLite.SQLiteCommand(commandText, connection)
Using reader = command.ExecuteReader()
action(reader)
End Using
End Using
End Using
End Sub
Then you can call the code like this:
UsingDataReader("/* your connection string here */", "SELECT * FROM settings", _
Sub (r)
Do While r.Read
'do something
Loop
End Sub)
This ensures that all of the disposable references are closed when the Sub has completed.
The first problem is that not all the disposables are being disposed of. We are assured that the connection passed to that helper is in a Using block, but the command also needs to be disposed of as it has a reference to the connection:
Dim cmd As New SQLiteCommand(sql, dbcon)
Even if you dont use the overloaded constructor, in order to work, somewhere you set the connection property. This illustrates one of the problems with such "DB helper" methods: the DBConnection, DBCommand and DBReader objects work together very closely, but they are created in different methods with different scopes and you can't normally see if everything is being cleaned up properly.
The code posted will always fail because that DBCommand object - and by extension the DBConnection - are not disposed. But even if you clean up properly, pooling will keep the DBConnection alive for a while as jmcilhinney explains. Here are 2 fixes:
Clear the Pool
Using dbcon As New SQLiteConnection(LiteConnStr),
cmd As New SQLiteCommand(sql, dbcon)
dbcon.Open()
Dim n As Int32 = 0
Using rdr = cmd.ExecuteReader
While rdr.Read
' == DoSomething()
Console.WriteLine("{0} == {1}", n, rdr.GetString(0))
n += 1
End While
End Using
' Clears the connection pool associated with the connection.
' Any other active connections using the same database file will be
' discarded instead of returned to the pool when they are closed.
SQLiteConnection.ClearPool(dbcon)
End Using
File.Delete(sqlFile)
The dbCon and cmd objects are "stacked" into one Using statement to reduce indentation.
This will close and discard any and all connections in the pool, provided they have been Disposed - as well as any objects which reference them. If you use Dim cmd ... you will need to explicitly dispose of it.
Force Garbage Collection
I think this is much more ham-fisted, but it is included for completeness.
Using dbcon As New SQLiteConnection(LiteConnStr),
cmd As New SQLiteCommand(sql, dbcon)
...
Using rdr = cmd.ExecuteReader
...
End Using
End Using
GC.WaitForPendingFinalizers()
File.Delete(sqlFile)
This also works as long as everything has been properly disposed of. I prefer not to mess with GC unless absolutely necessary. The issue here is that clean up will not be limited to DBProvider objects but anything which has been disposed and is awaiting GC.
Yet a third workaround would be to turn off pooling, but you would still have to dispose of everything.
You are going to need to also close your cnUser connection to the database.
Closing/disposing the reader does not necessarily close/dispose the open connection.
my connection string is saved in a string variable names str
what i am trying to do is use an input from the user as part of the string
the parts i want to take from the user are the ID and PASS
i am simply trying to check the connection statues with the ID and the PASS as inputs from the user.
Dim str As String = "Data Source=DESKTOP;uid=ID;pwd=PASS;database=DB"
Dim conn As New SqlConnection(str)
Private Sub btnconnect_Click(sender As Object, e As EventArgs) Handles btnconnect.Click
PW = txtadminpass.Text
Try
conn.Open()
conn.Close()
MsgBox("GOOD")
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
i haven't had much like while using the + and & for the strings.
any help would be appreciated.
The SqlConnectionStringBuilder is an appropriate class to use in this case. You can add parts of the connection string to it via properties, so there is no chance of making mistakes:
Imports System.Data.SqlClient
Module Module1
Sub Main()
Dim csb As New SqlConnectionStringBuilder
csb.DataSource = "DESKTOP"
csb.InitialCatalog = "DB"
csb.UserID = "z"
csb.Password = "x"
' output "Data Source=DESKTOP;Initial Catalog=DB;User ID=z;Password=x" '
Console.WriteLine(csb.ToString())
Console.ReadLine()
End Sub
End Module
So, you need to check if the user is allowed to log into the database or not. The way you have followed looks good, you define the connection string based on the given ID and password, and you try to establish a connection, if it fails, the user can't log in, else he can do that.
However, the way you defined the string is wrong, you must use concatenation to preserve the ID and password values, try this,
Dim str As String = "Data Source=DESKTOP; uid=" & ID & "; pwd=" & PASS & ";database=DB"
Another way, which makes it easy to read:
Const CONN_STRING As String = "Data Source=DESKTOP;uid={0};pwd={1};database=DB"
Dim connString As String = String.Format(CONN_STRING, txtUserID.Text.Trim, txtPassword.Text)
I'm trying to connect to a database and keep the connection open for any amount of user activity/queries within the database. Currently I have a connection that opens and closes for any query (save, update, etc...) which requires the logon process to the back-end every time the user saves etc... Is there a way to simply connect and leave the connection open so there won't be a lag time when running the query due to the logon process? This is what I'm using:
Private sConStrFormat As String = "Provider=TDOLEDB;Data Source=TDDEV;Persist Security Info=True;User ID={0};Password={1};Default Database=bcpm_ddbo;Session Mode=ANSI;"
Private Sub cmdsave_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdsave.Click
Dim sSQL As String
Dim sConStr As String
Dim user As String
Dim pass As String
user = txtuser.Text
pass = txtpass.Text
Dim UserName As String = user
Dim Password As String = pass
sConStr = String.Format(sConStrFormat, UserName, Password)
Using Con As System.Data.OleDb.OleDbConnection = New System.Data.OleDb.OleDbConnection(sConStr)
Con.Open()
sSQL = "INSERT INTO LTC_FBS (CLM_NUM) VALUES ('" & Me.txtClaim.Text & "')"
Dim cmdins As New System.Data.OleDb.OleDbCommand(sSQL, Con)
cmdins.ExecuteNonQuery()
Con.Close()
End Using
End Sub
.Net automatically maintains a connection pool for you. According to MSDN, when you call Close() on the Connection the framework
releases the connection to the
connection pool, or closes the
connection if connection pooling is
disabled
In vb.net 4.5 do the following:
At the top straight after the class definer put in this line:
Public Shared conn As OdbcConnection
Then, in the subs where you want to use the connection use this line:
If conn Is Nothing Then conn = New OdbcConnection(<your_connection_string>): conn.Open()
Then the New OdbcCommand will use the existing connection without opening a new one. Don't close the connection in your script until you are quite certain you're finished with it (_Shutdown is a good spot and you're good to go.
This also solves problems with MySQL when constantly opening new connections causes the max connections error.
Instead of defining 'con' in the using statement, define it up above as a static variable. When the function is called, you can see if 'Con' has been assined or not. If not, you build your connection string and set Con = New OleDBConnection, and open it. Since it's a static variable, it will retain its value at the next call. Check that it's not nothing, and then use it right away. Make sure you don't call close in the routine or the connection will not remain open.
Basically i have a query string that when i hardcode in the catalogue value its fine. when I try adding it via a variable it just doesn't pick it up.
This works:
Dim WaspConnection As New SqlConnection("Data Source=JURA;Initial Catalog=WaspTrackAsset_NROI;User id=" & ConfigurationManager.AppSettings("WASPDBUserName") & ";Password='" & ConfigurationManager.AppSettings("WASPDBPassword").ToString & "';")
This doesn't:
Public Sub GetWASPAcr()
connection.Open()
Dim dt As New DataTable()
Dim username As String = HttpContext.Current.User.Identity.Name
Dim sqlCmd As New SqlCommand("SELECT WASPDatabase FROM dbo.aspnet_Users WHERE UserName = '" & username & "'", connection)
Dim sqlDa As New SqlDataAdapter(sqlCmd)
sqlDa.Fill(dt)
If dt.Rows.Count > 0 Then
For i As Integer = 0 To dt.Rows.Count - 1
If dt.Rows(i)("WASPDatabase") Is DBNull.Value Then
WASP = ""
Else
WASP = "WaspTrackAsset_" + dt.Rows(i)("WASPDatabase")
End If
Next
End If
connection.Close()
End Sub
Dim WaspConnection As New SqlConnection("Data Source=JURA;Initial Catalog=" & WASP & ";User id=" & ConfigurationManager.AppSettings("WASPDBUserName") & ";Password='" & ConfigurationManager.AppSettings("WASPDBPassword").ToString & "';")
When I debug the catalog is empty in the query string but the WASP variable holds the value "WaspTrackAsset_NROI"
Any idea's why?
Cheers,
jonesy
alt text http://www.freeimagehosting.net/uploads/ba8edc26a1.png
I can see a few problems.
You are using concatenation in a SQL statement. This is a bad practice. Use a parameterized query instead.
You are surrounding the password with single quotes. They are not needed and in fact, I'm surprised it even works assuming the password itself does not have single quotes.
You should surround classes that implement IDisposable with a Using block
You should recreate the WASP connection object in GetWASPcr like so:
Public Sub GetWASPAcr()
Dim username As String = HttpContext.Current.User.Identity.Name
Dim listOfDatabaseConnectionString As String = "..."
Using listOfDatabaseConnection As SqlConnection( listOfDatabaseConnectionString )
Using cmd As New SqlCommand("SELECT WASPDatabase FROM dbo.aspnet_Users WHERE UserName = #Username")
cmd.Parameters.AddWithValue( "#Username", username )
Dim dt As New DataTable()
Using da As New SqlDataAdapter( cmd )
da.Fill( dt )
If dt.Rows.Count = 0 Then
WaspConnection = Null
Else
Dim connString As String = String.Format("Data Source=JURA;Initial Catalog={0};User Id={1};Password={2};" _
, dt.Rows(0)("WASPDatabase") _
, ConfigurationManager.AppSettings("WASPDBUserName") _
, ConfigurationManager.AppSettings("WASPDBPassword"))
WaspConnection = New SqlConnection(connString);
End If
End Using
End Using
End Using
End Sub
In this example, listOfDatabaseConnectionString is the initial connection string to the central database where it can find the catalog name that should be used for subsequent connections.
All that said, why would you need a class level variable to hold a connection? You should make all your database calls open a connection, do a sql statement, close the connection. So, five database calls would open and close a connection five times. This sounds expensive except that .NET gives you connection pooling so when you finish with a connection and another is requested to be opened, it will pull it from the pool.
Your string passed into the constructor for this SqlConnection object will be evaluated when the class is instantiated. Your WASP variable (I'm assuming) won't be set until the method you have shown is called.
Might want to quit looking one you have found your database:
For i As Integer = 0 To dt.Rows.Count - 1
If dt.Rows(i)("WASPDatabase") Is DBNull.Value Then
WASP = ""
Else
WASP = "WaspTrackAsset_" + dt.Rows(i)("WASPDatabase")
break
End If
Next
[link text][1]You are building your string on the fly by adding the value of a column to a string. So, for the row in question for the column "WASPDatabase" was tacked on to your string. So you got what it had. On another note, your earlier query of "select ... from ... where ..." where you are manually concatinating the string of a variable makes you WIDE OPEN to SQL-Injection attacks.
Although this link [1]: how to update a table using oledb parameters? "Sample query using parameterization" is to a C# sample of querying with parameterized values, the similar principles apply to most all SQL databases.
At the time you're creating the new connection, WASP is holding the value you want it to be holding? It is a string data type? Try adding .ToString after WASP and see if that helps anything.
Interesting problem. =-)
The problem is, as Paddy already points out, that the WaspConnection object gets initialized before you even have the chance to call GetWASPAcr. Try this:
Public Sub GetWASPAcr()
'[...]
End Sub
Dim _waspConnection As SqlConnection
Public Readonly Property WaspConnection As SqlConnection
Get
If _waspConnection Is Nothing Then
GetWASPAcr()
_waspConnection = New SqlConnection("Data Source=JURA;Initial Catalog=" & WASP & ";User id=" & ConfigurationManager.AppSettings("WASPDBUserName") & ";Password='" & ConfigurationManager.AppSettings("WASPDBPassword").ToString & "';")
End If
Return _waspConnection
End Get
End Property