Set SqlParameter in VB.NET? - vb.net

I'm new with classes and I want to create a SqlCommandManager class and I can't figure out how to pass SqlParameter on my class.
For example if I want to insert data I would just use my class like client below.
'Client
Dim m_SqlComManager as new SQLCommandManager("MyConnectionString")
m_SqlCommandManager.Commandtext = "INSERT INTO [TableName]([Field1],[Field2])VALUES(#Field1,Field2);"
m_SqlCommandManager.Parameters.AddWithValue("#Field1","SomeValue1")
m_SqlCommandManager.Parameters.AddWithValue("#Field2","SomeValue2")
m_SqlCommandManager.ExecuteNonQuery()
'Here is my class
Imports System.Data.SqlClient
Public Class SQLCommandManager
Private m_SqlParameters As SqlParameter()
Private m_Commandtext As String
Private m_ConStr As String
Public WriteOnly Property SQlParameter() As SqlParameter()
Set(ByVal value As SqlParameter())
value = m_SqlParameters
End Set
End Property
Public Property CommandText() As String
Get
Return m_Commandtext
End Get
Set(ByVal value As String)
value = m_Commandtext
End Set
End Property
Public Sub New(ByVal con As String)
m_ConStr = con
End Sub
Public Sub ExecuteNonQuery()
Using con As New SqlConnection(m_ConStr)
Using com As New SqlCommand
com.Connection = con
com.CommandText = m_Commandtext
'Please help
'How can i insert parameter here from client..
If con.State = ConnectionState.Closed Then
con.Open()
End If
com.ExecuteNonQuery()
End Using
End Using
End Sub
End Class
How how can I set the parameters before the ExecuteNonQuery method?
Thanks in advance..

I would do something like this:
Public Class SqlCommandManager
Private m_SqlParameters As List(Of SqlParameter)
Private m_Commandtext As String
Private m_ConStr As String
Public Sub New()
m_SqlParameters = New List(Of SqlParameter)()
End Sub
Public ReadOnly Property SqlParameters() As List(Of SqlParameter)
Get
Return m_SqlParameters
End Get
End Property
Public Property CommandText() As String
Get
Return m_Commandtext
End Get
Set
value = m_Commandtext
End Set
End Property
Public Sub New(con As String)
m_ConStr = con
End Sub
Public Sub ExecuteNonQuery()
Using con As New SqlConnection(m_ConStr)
Using com As New SqlCommand(m_Commandtext, con)
com.Parameters.AddRange(m_SqlParameters.ToArray())
con.Open()
com.ExecuteNonQuery()
con.Close()
End Using
End Using
End Sub
End Class
What I've changed:
Changed the class name to SqlCommandManager to be in line with Microsoft's recommendations (don't capitalize more than 2 letters in an abbreviation; IO is fine, Sql and Xml should not be all capitalized)
I would use a List(Of SqlParameter) rather than an array - much easier to deal with, much easier to add additional parameters to it
I prefer to pass the CommandText and the SqlConnection right into the constructor of the SqlCommand - that way,you definitely never forget these two vital bits of information!
Just before your .ExecuteQuery, add the parameters defined in your list to the parameter array of the SqlCommand using a single call to .AddRange()

Related

Populate class from query on VB Net

Help translate C# code from this link Simplest way to populate class from query in C# to VB Net.
Option Infer On
Imports System.Reflection
Private Sub Main()
Dim connectionString = "..."
Dim records = (New Query(connectionString)).SqlQuery(Of TVChannel)("select top 10 * from TVChannel")
End Sub
Private Class TVChannel
Public Property number() As String
Public Property title() As String
Public Property favoriteChannel() As String
Public Property description() As String
Public Property packageid() As String
Public Property format() As String
End Class
Public Class Query
Private ReadOnly _connectionString As String
Public Sub New(ByVal connectionString As String)
_connectionString = connectionString
End Sub
Public Function SqlQuery(Of T)(ByVal query As String) As List(Of T)
Dim result = New List(Of T)()
Using connection = New SqlConnection(_connectionString)
connection.Open()
Using command = connection.CreateCommand()
command.CommandText = query
Using reader = command.ExecuteReader()
Dim columns = Enumerable.Range(0, reader.FieldCount).Select(Function(f) reader.GetName(f)).ToArray()
Dim properties = GetType(T).GetProperties()
Do While reader.Read()
Dim data = New Object(reader.FieldCount - 1){}
reader.GetValues(data)
Dim instance = DirectCast(Activator.CreateInstance(GetType(T)), T)
For i = 0 To data.Length - 1
If data(i) Is DBNull.Value Then
data(i) = Nothing
End If
Dim [property] = properties.SingleOrDefault(Function(x) x.Name.Equals(columns(i), StringComparison.InvariantCultureIgnoreCase))
If [property] IsNot Nothing Then
[property].SetValue(instance, Convert.ChangeType(data(i), [property].PropertyType))
End If
Next i
result.Add(instance)
Loop
End Using
End Using
End Using
Return result
End Function
End Class
but, I got error on this line
Dim instance = DirectCast(Activator.CreateInstance(GetType(T)), T)
System.MissingMethodException: 'No parameterless constructor defined for this object.'
This is a much better pattern to follow. It addresses at least four issues in the original code (sql injection, Nothing vs null, constructor access, unnecessary allocations):
Public Module SQL
Private ReadOnly _connectionString As String = "..."
Public Iterator Function Query(Of T)(ByVal query As String, translate As Func(IDataRecord, T), ParamArray data() As SqlParameter) As IEnumerable(Of T)
Using connection As New SqlConnection(_connectionString), _
command As New SqlCommand(query, connection)
If data IsNot Nothing Then command.Parameters.AddRange(data)
connection.Open()
Using reader As SqlDataReader = command.ExecuteReader()
While reader.Read()
Yield translate(reader)
End While
reader.Close()
End Using
End Using
End Function
End Module
Call it like this:
Private Sub Main()
Dim records = SQL.Query("select top 10 * from TVChannel",
Function(r)
'Yes, you're doing the mapping manually now for each query.
'But this lets you properly account for things NULL, column name mismatches, computed properties, etc.
Return New TVChannel With {
.number = r["number"],
.title = r["title"],
.favoriteChannel = r["favoriteChannel"],
.description = r["description"],
.packageid = r["packageid"],
.format = r["format"]
}
End Function,
Nothing)
For Each channel As TVChannel In records
Console.WriteLine($"Channel {channel.number}, {channel.title}")
Next
End Sub

VB.NET connection to MySQL Add Model

I'm very new to .NET. I am trying to use a code example from the first person that posted a response, here: Connect to remote MySQL database using VB.NET 2010
I would like to instantiate the MySqlVB model object but when I add the following code into the controller, I get a not found error. I don't know how to resolve this.
The error is: Warning 1 Namespace or type specified in the Imports 'MySql.Data.MySqlClient' doesn't contain any public member or cannot be found. Make sure the namespace or the type is defined and contains at least one public member. Make sure the imported element name doesn't use any aliases.
What I need is to run a MySQL query and to return the dataset to the controller. Can someone show me how to do this, please?
I'm using VB 2010 Express to do this.
This is the controller
Public Class Form1
Private Sub PrintBtn_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles PrintBtn.Click
Dim data As New MySqlVB
With data
If .Connection Then
MessageBox.Show("Database Conneted.")
Else
MessageBox.Show(.ErrorMessage)
End If
End With
End Sub
End Class
And this is my model object
Imports MySql.Data.MySqlClient
Public Class MySqlVB
Private _connection As New MySqlConnection
Private _errormessge As String
Private _servername As String = "xxx.xxx.xxx.xxx"
Private _databasename As String = "testdb"
Private _userid As String = "theuser"
Private _password As String = "thepass"
Public WriteOnly Property ServerName() As String
Set(ByVal value As String)
_servername = value
End Set
End Property
Public WriteOnly Property DatabaseName() As String
Set(ByVal value As String)
_databasename = value
End Set
End Property
Public WriteOnly Property UserID() As String
Set(ByVal value As String)
_userid = value
End Set
End Property
Public WriteOnly Property Password() As String
Set(ByVal value As String)
_password = value
End Set
End Property
Public ReadOnly Property ErrorMessage() As String
Get
Return _errormessge
End Get
End Property
Public Function Connection() As Boolean
Try
_connection.ConnectionString = "Server=" & _servername & ";Port=3306;Database=" & _databasename & ";User ID=" & _userid & ";Password=" & _password & ""
_connection.Open()
If _connection.State = ConnectionState.Open Then
_connection.Close()
Return True
End If
Catch ex As Exception
_errormessge = ex.Message
Return False
End Try
End Function
End Class
Assuming you have fixed your reference to MySql.Data.MySqlClient I think your class could use some work.
Public Class DataAccess
Private ConnectionString As String
Public Sub New(UserName As String, Password As String)
Dim builder As New MySqlConnectionStringBuilder With {
.Server = "xxx.xxx.xxx.xxx",
.Database = "testdb",
.UserID = UserName,
.Password = Password
}
ConnectionString = builder.ConnectionString
Debug.Print(ConnectionString) 'just to see what the builder created
End Sub
Public Function TestConnecion() As Boolean
Using cn As New MySqlConnection(ConnectionString)
Try
cn.Open()
Catch ex As Exception
Debug.Print(ex.Message) 'just to see what is wrong with connection
Return False
End Try
End Using
Return True
End Function
Public Function GetData() As DataTable
Dim dt As New DataTable
Using cn As New MySqlConnection(ConnectionString)
Using cmd As New MySqlCommand("Select * From SomeTable")
cn.Open()
dt.Load(cmd.ExecuteReader)
End Using
End Using
Return dt
End Function
End Class
Assuming you have a DataGridView to display data and 2 text boxes for user id and password, you can use your class in your form like this.
Private Sub FillGrid()
Dim daClass As New DataAccess(txtUser.Text, txtPassword.Text)
Dim dt = daClass.GetData
DataGridView1.DataSource = dt
End Sub
Of course you will need to add error handling. Also you need to salt and hash passwords. Plain text passwords should never be stored.

How to get product by its ID?

Database:Microsoft Access Created Settings called ConnStr with value
provider=Microsoft.ACE.OLEDB.12.0; Data Source=c:\sm.accdb Product
table has 4 columns:ProductID, Description, Category, Price I have 3
classes:Manager, Product, ProductManager
Public MustInherit Class Manager
Private _connectionString As String
Protected Property ConnectionString() As String
Get
Return _connectionString
End Get
Set(ByVal value As String)
_connectionString = value
End Set
End Property
Public Sub New(ByVal connStr As String)
ConnectionString = connStr
End Sub
End Class
Public Class Product
Private _id As Integer
Public Property ID() As Integer
Get
Return _id
End Get
Set(ByVal value As Integer)
_id = value
End Set
End Property
Private _description As String
Public Property Description() As String
Get
Return _description
End Get
Set(ByVal value As String)
_description = value
End Set
End Property
Private _category As String
Public Property Category() As String
Get
Return _category
End Get
Set(ByVal value As String)
_category = value
End Set
End Property
Private _price As Double
Public Property Price() As Double
Get
Return _price
End Get
Set(ByVal value As Double)
_price = value
End Set
End Property
End Class
Imports System.Data.OleDb
Public Class ProductManager
Inherits Manager
Public Function GetProductByID(ByVal id As Integer) As Product
Dim con = New System.Data.OleDb.OleDbConnection
Dim sql As String = "SELECT * FROM Product WHERE ProductID=#id"
con.Open()
Try
Dim description As String
Dim category As String
Dim price As Double
Dim cmd As New System.Data.OleDb.OleDbCommand(sql, con)
cmd.Parameters.Add(id.ToString, "#id")
cmd.Parameters.Add(description, "#description")
cmd.Parameters.Add(category, "#category")
cmd.Parameters.Add(price.ToString, "#price")
cmd.ExecuteNonQuery()
cmd.Dispose()
cmd = Nothing
Catch ex As Exception
Throw New Exception(ex.ToString(), ex)
Finally
con.Close()
End Try
Return nothing
End Function
End Class
I could not get product from database! I think problem is in ProductManager Class! I am so confused! Please help me!
You must use cmd.ExecuteReader() method
Dim myReader As OledbDataReader = cmd.ExecuteReader(CommandBehavior.CloseConnection)
While myReader.Read()
Console.WriteLine(myReader.GetString(0))
End While
myReader.Close()

transaction not rolling back - scope issue?

I'm having a hard time understanding why I can't get this transaction to roll back. I have a DataAccess object that handles the connection/transaction. I have a MailingData business object, and a MailingDataAccess object which takes the DataAccess object byRef from the MailingData object and executes a stored procedure. I immediately rollback the update, but it doesn't roll back. I'm guessing this is a scope issue. Help? I was doing byval with dataaccess, but that wasn't working so I changed to byref.
vb.net console app -
code:
Dim da As DataAccess = New DataAccess("mydb")
Try
' business object
Dim mailingData as New MailingData()
' start a transaction
da.StartTransaction()
' do stuff, set properties...
' pass the data access object byref to a method that calls a stored proc
mailingData.UpdateCategorize(da)
' testing, this doesn't roll back the update from the above stored proc
da.RollBackTransaction()
'da.CommitTransaction()
Catch ex As Exception
da.RollBackTransaction()
End Try
My data access object:
Private _database As Database
Private _transaction As Data.IDbTransaction
Private _connection As Data.IDbConnection
Public ReadOnly Property Database() As Database
Get
Return _database
End Get
End Property
Public ReadOnly Property Connection() As IDbConnection
Get
Return _connection
End Get
End Property
Public ReadOnly Property Transaction() As Data.IDbTransaction
Get
Return _transaction
End Get
End Property
Public Sub New()
_database = GetDatabase()
End Sub
Public Sub New(ByVal database As String)
_database = GetDatabase(database)
End Sub
Private Function GetDatabase() As Database
Return DatabaseFactory.CreateDatabase()
End Function
Private Function GetDatabase(ByVal database As String) As Database
Return DatabaseFactory.CreateDatabase(database)
End Function
'-- if not transaction started, start new one.
Public Sub StartTransaction()
If _transaction Is Nothing Then
_connection = _database.CreateConnection()
_connection.Open()
_transaction = _connection.BeginTransaction
End If
End Sub
Public Sub CommitTransaction()
_transaction.Commit()
If Not IsNothing(Connection) Then
Connection.Close()
End If
End Sub
Public Sub RollBackTransaction()
_transaction.Rollback()
If Not IsNothing(Connection) Then
Connection.Close()
End If
End Sub
End Class
mailingData.UpdateCategorize() is defined within the mailingData business object as:
Public Function UpdateCategorize(ByRef da As DataAccess) As Integer
Dim mailingDA As New MailingDataAccess
Return mailingDA.UpdateCategorize(da, Me)
End Function
MailingDataAccess.UpdateCategorize is defined as:
Public Function UpdateCategorize(ByRef da As DataAccess, ByVal clsMailing As MailingData) As Integer
Dim db As Database = da.Database
Dim DbCommand As DbCommand
Dim dbStatus As Integer = 0
DbCommand = da.Database.GetStoredProcCommand("proc_UpdateCategorize")
With db
.AddInParameter(DbCommand, "#pMailingID", Data.DbType.Int32, clsMailing.MailingID)
dbStatus = db.ExecuteNonQuery(DbCommand)
Return dbStatus
End Function
I figured it out, this line:
dbStatus = db.ExecuteNonQuery(DbCommand)
should be:
dbStatus = db.ExecuteNonQuery(DbCommand, da.Transaction)

trying to query a database

I'm having a bit of a problem populating a collections class with the values from the database. Everytime I loop through a record in the WHILE DR.READ loop, the last record over writes all the other items in the collection. My returnVal collections has several of the same items despite the loop showing each individual record being added into returnVal. Thanks for any help.
Public Shared Function getStuff(ByVal sb As StringBuilder) As System.Collections.Generic.List(Of Minutes)
Dim returnVal As New System.Collections.Generic.List(Of Minutes)
Dim conn As New SqlConnection
Dim cmd As New SqlCommand
Dim dr As SqlDataReader
conn.ConnectionString = System.Configuration.ConfigurationManager.ConnectionStrings("ConnectionString").ConnectionString
cmd.Connection = conn
cmd.CommandText = sb.ToString
Try
conn.Open()
dr = cmd.ExecuteReader
While dr.Read
Dim _minutes As New Minutes
_minutes.Minutes = dr("minutes")
_minutes.MinutesId = dr("minutesId")
returnVal.Add(_minutes)
End While
Catch ex As Exception
Dim _minutes As New Minutes
_minutes.Minutes = ex.ToString
_minutes.MinutesId = 0
returnVal.Add(_minutes)
End Try
conn.Close()
Return returnVal
End Function
This is my Minutes Class
Imports Microsoft.VisualBasic
Public Class Minutes
Private Shared _minutesId As Integer
Private Shared _minutes As String
Public Property MinutesId() As Integer
Get
Return _minutesId
End Get
Set(ByVal value As Integer)
_minutesId = value
End Set
End Property
Public Property Minutes() As String
Get
Return _minutes
End Get
Set(ByVal value As String)
_minutes = value
End Set
End Property
Public Shared Function getStuff(ByVal sb As StringBuilder) As System.Collections.Generic.List(Of Minutes)
Return MinutesDA.getStuff(sb)
End Function
Public Shared Function modify(ByVal sb As StringBuilder) As String
Return MinutesDA.modify(sb)
End Function
Public Shared Property Id() As Integer
Get
Return MinutesDA.Id
End Get
Set(ByVal value As Integer)
MinutesDA.Id = value
End Set
End Property
Public Shared Property Index() As Integer
Get
Return MinutesDA.Index
End Get
Set(ByVal value As Integer)
MinutesDA.Index = value
End Set
End Property
End Class
The problem is that the fields in the Minutes class are shared. There will be one instance of the fields shared by all instances of the class. Remove the "Shared" from the fields:
Private _minutesId As Integer
Private _minutes As String
This will store the values for each instance separately
To confirm you are getting new values or the same value try this:
Dim i as Integer
i = 0
While dr.Read
Dim _minutes As New Minutes
_minutes.Minutes = dr("minutes")
' _minutes.MinutesId = dr("minutesId")
_minutes.MinutesId = i
i = i + 1
returnVal.Add(_minutes)
End While
After this you can see if the MinuteId is different.
N.B. In VB6 you had to set the _minutes to Nothing at the end of the loop to get a new instance. However I wouldn't have thought that would be true in VB.NET