Can I override SqlCommand functions? - vb.net

I have code that will be for example
Dim cmd As New SqlClient.SqlCommand(commandString, databaseString)
Dim result As Integer
Try
result = cmd.ExecuteScalar()
Catch e as Exception
'catch exception code
End Try
Instead of doing that, can I override the ExecuteScalar function to do some kind of generic exception catching?

No, but you can create a generic DoExecuteScalar helper function that takes a SQL string and connection string:
Public Function DoExecuteScalar(commandString as String, databaseString as String) as Object
Dim cmd as New SqlClient.SqlCommand(commandString, databaseString)
Dim result as Integer
Try
result = cmd.ExecuteScalar()
Catch e As Exception
Return 0
End Try
Return result
End Function

No you cannot override methods because you cannot inherit from it since SqlCommand is sealed.
What's wrong with catching (or throwing) exceptions where you're using the SqlCommand?

You could also use composition:
Public Class SqlCommandManager
Private _sqlCommand As System.Data.SqlClient.SqlCommand
Public Sub New(command As System.Data.SqlClient.SqlCommand)
If command Is Nothing Then
Throw New ArgumentNullException("command")
End If
_sqlCommand = command
End Sub
Private ReadOnly Property Command As System.Data.SqlClient.SqlCommand
Get
Return _sqlCommand
End Get
End Property
Public Function ExecuteScalar() As Object
Dim result As Object
Try
result = Command.ExecuteScalar()
Catch e As Exception
'catch exception code
End Try
Return result
End Function
End Class
Not saying this is the best alternative, just that it is one ....

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

Set SqlParameter in 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()

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)

how to declare method return value in vb.net

I've made a class named Cls_ICCID where I declare the method in Update_Status which returns a byRef variable.
Cls_ICCID
Public Sub Update_Status(**ByRef massege As String**, ByVal ICCID_No As Integer, ByVal status As Integer)
Try
Dim cmd As SqlCommand
Dim sql As String
Dim myConnection As SqlConnection = New SqlConnection()
myConnection.ConnectionString = "Data Source=TEHRANI\TEHRANI;Initial Catalog=GSMProduction;Persist Security Info=True;User ID=sa;Password=1"
sql = "UPDATE Tbl_ICCID SET Status=status WHERE ICCID=ICCIDNo"
myConnection = New SqlConnection(sql)
myConnection.Open()
cmd = New SqlCommand(sql, myConnection)
cmd.ExecuteNonQuery()
cmd.Dispose()
myConnection.Close()
massege = "SeccessFully"
Catch ex As Exception
massege = "server Error"
End Try
End Sub
And then I execute that method when a textbox change event triggers:
Private Sub Txt_ICCID_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Txt_ICCID.TextChanged
Dim clsICCID As Cls_ICCID
clsICCID.Update_Status(lblError.Text, Txt_ICCID.Text, 1)
End Sub
But this gives me the exception:
Arithmetic operation resulted in an overflow.
What am I doing wrong?
to be honest, as konrad said you should really work on the basics if you want to use such kind of code.
haven't tried the code out, but i think you want something like
Public Function Update_Status(iccidNo As Integer, status As Integer) As String
Const sql As String = "UPDATE Tbl_ICCID SET Status=#status WHERE ICCID=#ICCIDNo"
Const connstr As String = "Data Source=TEHRANI\TEHRANI;Initial Catalog=GSMProduction;Persist Security Info=True;User ID=sa;Password=1"
Try
Using myConnection = New SqlConnection()
myConnection.ConnectionString = connstr
Using cmd = New SqlCommand(sql, myConnection)
cmd.Parameters.AddWithValue("#status", status)
cmd.Parameters.AddWithValue("#ICCIDNo", iccidNo)
cmd.ExecuteNonQuery()
Update_Status = "Successfully"
End Using
End Using
Catch ex As SqlException
Update_Status = "server Error"
Catch ex As Exception
Update_Status = "server Error"
End Try
End Function
but personally i would handle the exceptions not that way and return a boolean.
The thing that you specifically ask is returning value in a method. Instead of Sub you must declare it like Function and then return the value with return:
Public Function Update_Status(ICCID_No As Integer, status As Integer) As String
'...
return massege
End Function
And then catch the value:
lblError.Text = clsICCID.Update_Status(Txt_ICCID.Text, 1)
I should use from Function instead of sub method. because sub method is void.
For a method to return a value you need to declare your method as a function, instead of sub.
For example:
Public Function Update_Status(ByVal ICCID_No As Integer, ByVal status As Integer) As String
Return "errorMessage"
End Function
And can then invoke it like this:
returnValue = Update_Status(Txt_ICCID.Text, 1)
see: http://msdn.microsoft.com/en-us/library/sect4ck6%28v=vs.80%29.aspx
But it also seems that your parameters might not match up: sending a String as an Integer?

VB.NET exception handling for something that returns a data set instead of a string

My program is throwing an error I should handle. But I cannot return a string message because the it is a function that returns a dataset:
Public Function getUserInfo(ByValue testUserIdAs String) As DataSet
Dim dsUseInfo As DataSet = New DataSet()
Try
Dim objTestWs As New TestWebService.UserMaintenanceSoapClient
dsUseInfo = objTestWs.dsGetUserInfo(TestOU, PAC, paramUserID)
Return (dsUseInfo)
Catch ex As Exception
' TEST FIX ERROR HANDLING -LIWM Please search how to return custom error. I want to return "userid already exists"
Throw
End Try
I was thinking of putting in:
If error
then return "Error Message"
But I can't return it as type string.
It looks like you don't really know what to do with the exception in getUserInfo and just want to pass it on to the outer function.
Well, here's the great thing about exceptions: They are passed on automatically! There's nothing you need to do, and, in particular, you do not need to use the return value of the function for that. Just don't catch the exception until you know what to do with it.
For example, in your case, just handle the error in the calling function instead:
Remove the error handling code from the called function:
Public Function getUserInfo(ByValue testUserIdAs String) As DataSet
Dim objTestWs As New TestWebService.UserMaintenanceSoapClient
Return objTestWs.dsGetUserInfo(TestOU, PAC, paramUserID)
End Function
and add it to the calling function, i.e., replace
...
Dim dsUserInfo As DataSet
dsUserInfo = getUserInfo()
...
with
...
Dim dsUserInfo As DataSet
Try
dsUserInfo = getUserInfo()
Catch ex As Exception
' Do whatever you want to do in case of an error here
MsgBox("Could not get User Info: " & ex.Message)
Return
End Try
...
One you are familiar with this technique, you can go on to more advanced topics like throwing your own exception, like #Justin suggested. Just make sure to include information about the original cause of the error in your own exception, for example, by copying parts of ex.Message into your own exception message and setting the innerException property.
You could throw a generic exception:
Public Function getUserInfo(ByValue testUserIdAs String) As DataSet
Dim dsUseInfo As DataSet = New DataSet()
Try
Dim objTestWs As New TestWebService.UserMaintenanceSoapClient
dsUseInfo = objTestWs.dsGetUserInfo(TestOU, PAC, paramUserID)
Return (dsUseInfo)
Catch ex As Exception
Throw New Exception("Custom message", ex)
End Try
This will set the message on the exception you catch up the call stack to be "Custom Message" with an inner exception that contains the original exception which was thrown.
Or you could create a custom exception(by inheriting from System.Exception) elsewhere if you want to throw a more expressively named exception(and so you don't have to catch all exceptions when you want to catch this custom type).
Public Class UserInfoNotFoundException
Inherits System.Exception
Public Sub New()
End Sub
Public Sub New(message As String)
MyBase.New(message)
End Sub
Public Sub New(message As String, innerException As Exception)
MyBase.New(message, innerException)
End Sub
End Class
You could then, for example, throw a UserInfoNotFoundException.
I would change the whole approach to exceptions and errors:
Create a new assembly (project) and reference it in your application
Declare new shared events which accept your personal datatype (referenced assembly)
Declare at least one shared function (referenced assembly) which does a RaiseEvent on your new events
Add handlers (main application) for your shared events in which you react accordingly
Call your function from within the main application,
passing your own parameters whenever you need to throw errors /
exceptions
This way you circumvent many programming mistakes and centralize error and exception-handling.
Public Module IDENTIFIERS
Public Enum EvtMsg
ERR_MYERR
ERR_MYERR2
End Enum
Public Enum EvtClass
EXCEPTION
ERR
End Enum
End Module
Public Class Events
Shared Event Err(ByVal code As EvtMsg)
Shared Event Exception(ByRef iEx As Exception)
Public Shared Sub Raise(ByVal iEvtClass As EvtClass, ByVal iMsg As EvtMsg, Optional ByRef iEx As Exception = Nothing)
If Not [Enum].IsDefined(GetType(EvtClass), iEvtClass) Then
Dim ex As New ArgumentOutOfRangeException("unbekannte Event-Klasse '" & iEvtClass.ToString & "' übergeben", "iEvtClass")
RaiseEvent Exception(ex)
End If
If Not [Enum].IsDefined(GetType(EvtMsg), iMsg) Then
Dim ex As New ArgumentOutOfRangeException("unbekannte Event-Msg '" & iMsg.ToString & "' übergeben", "iMsg")
RaiseEvent Exception(ex)
End If
Select Case iEvtClass
Case EvtClass.ERR
RaiseEvent Err(iMsg)
Case EvtClass.EXCEPTION
If iEx IsNot Nothing Then
RaiseEvent Exception(iEx)
Else
Dim ex As New MissingFieldException("Raise() ohne Exception aufgerufen, iMsg : " & iMsg & "EvtClass : " & iEvtClass.ToString(), "iEx")
RaiseEvent Exception(ex)
End If
End Select
End Sub
End Class
And now you can easily use these error-handlers in any assembly which references your error-assembly:
Constructor
AddHandler Events.Err, AddressOf Err
AddHandler Events.Exception, AddressOf Except
Class-body
Private Sub Except(ByRef iEx As Exception)
'do your stuff here
End Sub
Private Sub Err(ByVal Err As EvtMsg)
'do your stuff here
End Sub