Setting database class at run time in VB.Net - vb.net

I have the follow classes for Sqlite and SqlServer:
Class for SQLite:
Imports System.Data.SQLite
Public Class clsOperDB_SQLite
Public Shared Function getValue(sql As String) As String
Try
Using conn As New SQLiteConnection(strConn_SQLITE)
Using cmd As New SQLiteCommand()
cmd.Connection = conn
conn.Open()
cmd.CommandText = sql
Return cmd.ExecuteScalar
End Using
End Using
Catch ex As Exception
MsgBox(ex.Message)
End Try
Return ""
End Function
End Class
Class for SQLSERVER:
Imports System.Data.SqlClient
Public Class clsOperDB_SQLSERVER
Public Shared Function getValue(sql As String) As String
Try
Using conn As New SqlConnection(strConn_SQLSERVER)
Using cmd As New SqlCommand()
cmd.Connection = conn
conn.Open()
cmd.CommandText = sql
Return cmd.ExecuteScalar
End Using
End Using
Catch ex As Exception
MsgBox(ex.Message)
End Try
Return ""
End Function
End Class
this is my test form:
Public Class Form1
'form level variable
Dim dbConnector
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim connectionType As String = "SQLITE"
' need something like this or any way to set form level variable
If connectionType = "SQLITE" Then
dbConnector = clsOperDB_SQLite
Else
dbConnector = clsOperDB_SQLSERVER
End If
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'get value from SQLITE
Dim ValueFromDatabase As String = dbConnector.getValue("select .....")
End Sub
End Class
I need help to define dbConnector variable and set its value, also intellisense should show me class methods, using a parameter I want to change database and avoid using a conditional for every time I want to use one or the other database :
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim Query As String = "Select ..."
Dim ValueFromDatabase As String = ""
' dont want to use if for each sql query
If connectionType = "SQLITE" Then
ValueFromDatabase = clsOperDB_SQLite.getValue(Query)
Else
ValueFromDatabase = clsOperDB_SQLSERVER.getValue(Query)
End If
End Sub
The rest of methods and params for both classes are the same only change class data objects (SQLiteConnection, SqlConnection, and so)
Thanks

You should define an interface that species all the common members. You can then create a class for each data source that implements that interface. In your application code, you can then declare a variable of that interface type and assign an instance of any class that implements it to that variable. You can then just use that variable and invoke any member of the interface without caring what type the actual class instance is.
The interface and the implementing classes would look something like this:
Public Interface IDataAccessProvider
Function GetValue(sql As String) As String
End Interface
Public Class SqliteDataAccessProvider
Implements IDataAccessProvider
Public Function GetValue(sql As String) As String Implements IDataAccessProvider.GetValue
'Provide SQLite-specific implementation here.
End Function
End Class
Public Class SqlServerDataAccessProvider
Implements IDataAccessProvider
Public Function GetValue(sql As String) As String Implements IDataAccessProvider.GetValue
'Provide SQL Server-specific implementation here.
End Function
End Class
Your application code might then look like this:
Private dataAccessProvider As IDataAccessProvider
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'Read a value that identifies the data source and store it here.
'The value might be stored in the config file or wherever else is appropriate.
Dim dataSourceIdentifier As String
Select Case dataSourceIdentifier
Case "SQLite"
dataAccessProvider = New SqliteDataAccessProvider()
Case "SQL Server"
dataAccessProvider = New SqlServerDataAccessProvider()
End Select
End Sub
You can then just call dataAccessProvider.GetValue in your code without any care for what the data source actually is, except to ensure that your SQL syntax is valid for that data source.
Please note that, while what you do is up to you, I have chosen to use a sane naming convention in this code. No one would last long working for me using class names like clsOperDB_SQLSERVER. There's a reason you don't see any types with names like that in the .NET Framework.

Related

How to Setup A Combox , always will with same data, as a user control to be used on multiple forms

I have a ComboBox that I use on multiple WinForms. Instead of dropping a ComboBox on each WinForm and then filling the ComboBox with data from a DataTable on each individual WinForm, couldn't I create a User Control (ComboBox) that has the data populated already and just use that UC on my Winforms?
Below is how I fill the data for each individual combobox now. (I have a public class for the sql stuff)
The Variable SQL comes from a Class called SQLControl. the Class has all the sql connection stuff.
Public Sub Fillcombobox()
sql.AddParam("#ExaminerType", 3)
sql.ExecQuery("MyStoredProcedure")
ComboBoxExaminer.ValueMember = "Examiner_ID"
ComboBoxExaminer.DisplayMember = "Last_Name"
ComboBoxExaminer.DataSource = sql.DBDT
End Sub
Private Sub MyWinform_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Call Fillcombobox()
End Sub
You can put a small Class Examiner
Public Class Examiner
Public Property Examiner_ID As Integer
Public Property Last_Name As String
Public Sub New(ID As Integer, lname As String)
Examiner_ID = ID
Last_Name = lname
End Sub
End Class
Then, when the first form loads, get the data in a list declared in a module so it can be accessed from any form in the application. Of course, you may have other things in the Module.
Module Module1
Public ExaminerData As New List(Of Examiner)
End Module
Private Sub MyWinform_Load(sender As Object, e As EventArgs) Handles MyBase.Load
FillExaminerList()
ComboBoxExaminer.ValueMember = "Examiner_ID"
ComboBoxExaminer.DisplayMember = "Last_Name"
ComboBoxExaminer.DataSource = ExaminerData
End Sub
Any other form that needs the data to fill a combo box can use ExaminerData. You only call FillExaminerList once at the beginning of the application. There is only a single hit on the database.
Private OPConStr As String = "Your connection string."
Private Sub FillExaminerList()
Dim dt As New DataTable
Using cn As New SqlConnection(OPConStr),
cmd As New SqlCommand("MyStoredProcedure", cn)
cmd.Parameters.Add("#ExaminerType", SqlDbType.Int).Value = 3
Using reader = cmd.ExecuteReader
dt.Load(reader)
End Using
End Using
For Each row As DataRow In dt.Rows
Dim ex As New Examiner(CInt(row("Examiner_ID")), row("Last_Name").ToString)
ExaminerData.Add(ex)
Next
End Sub

How do you dispose a local SqlConnection?

Im programming in vb.net
I want to read data from an mssql database. I want to make it flexible for several different queries, so I put the connecting part into a separate class. Whenever I want to make a query I can get with it a preconfigured DataAdapter.
But because of this separation I dont know how to correctly dispose my SqlConnection after collecting the data.
An examplatory use:
Public Class Form1
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim DBA As New DBAccess("dummycommand")
DBA.provideAdapter.Fill(dummytable)
...Dispose? 'This is the part where you usually dispose your datacollecting ressources
End Sub
End Class
Friend Class DBAccess
Private SqlString As String
Friend Sub New(ByVal sql As String)
SqlString = sql
End Sub
Friend Function provideAdapter() As SqlDataAdapter
Dim cn As New SqlConnection("dummyconstring")
Dim da As New SqlDataAdapter(SqlString, cn)
Return da
End Function
End Class
Can you tell me how I change this concept to fit a dispose?
You could make your data access class Disposable
Friend Class DBAccess
Implements IDisposable
Private ReadOnly sqlString As String
Private disposedValue As Boolean
Private cn As SqlConnection
Private da As SqlDataAdapter
Friend Sub New(sql As String)
sqlString = sql
End Sub
Friend Function provideAdapter() As SqlDataAdapter
cn = If(cn, New SqlConnection("dummyconstring"))
da = If(da, New SqlDataAdapter(SqlString, cn))
Return da
End Function
Protected Overridable Sub Dispose(disposing As Boolean)
If Not disposedValue Then
If disposing Then
da?.Dispose()
cn?.Dispose()
End If
disposedValue = True
End If
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(disposing:=True)
GC.SuppressFinalize(Me)
End Sub
End Class
and use it like this
Using DBA As New DBAccess("dummycommand")
DBA.provideAdapter.Fill(dummytable)
End Using ' Will automatically call Dispose here
but in the long run you could look at an ORM such as Entity Framework to make your life easier.

Sharing variables across classes and forms

I have a class called DataStorage that stores all of the publicly declared variables that I use in my program. My main class calls a public array from DataStorage and fills it with values. The main class then calls up a windows form and populates the textboxes on it with the values in the array. However, when I run the program, the form comes up with blank textboxes. I don't understand why this would be the case. I also tried populating the textboxes from the load function of the form rather than in the main class. I debugged and stepped through my code, and the main class filled the array with values fine. But when the form was called, and I started stepping through the code to populate the textboxes, it said the array was equal to nothing. If the array is stored in a separate class that both the main class and the form call, why are the values getting set to null between the main class and the form?
Here is what my code looks like:
Public Class Main
Private ds As New DataStorage
Public Sub Test()
For i = 0 to 2
ds.Info(i) = "Hello"
Next
Form1.ShowDialog()
End Sub
End Class
Public Class DataStorage
Public Info() As String
End Class
Public Class Form1
Private ds As New DataStorage
Private Sub Form1_Load(sender As Object, e As EventAgrs) Handles MyBase.Load
Textbox1.Text = ds.Info(0)
Textbox2.Text = ds.Info(1)
Textbox3.Text = ds.Info(2)
End Sub
I just called it Form1; the program starts with the sub Test in the main class.
I just need a way to have variables/arrays that are accessible by any class or form and don't change value or get set to null unless I tell it to.
You must give your array a size before you try to fill it.
Public Class TestArray
Private ds As New DataStorage
Private Sub TestArray_Load(sender As Object, e As EventArgs) Handles MyBase.Load
FillArray()
TextBox1.Text = ds.Info(0)
TextBox2.Text = ds.Info(1)
TextBox3.Text = ds.Info(2)
End Sub
Public Sub FillArray()
For i = 0 To 2
ds.Info(i) = "Hello"
Next
End Sub
End Class
Public Class DataStorage
Public Info(2) As String
End Class
If you don't want to give a size then use a List(Of T)
Public Class TestArray
Private ds As New DataStorage
Private Sub TestArray_Load(sender As Object, e As EventArgs) Handles MyBase.Load
FillList()
TextBox1.Text = ds.Info(0)
TextBox2.Text = ds.Info(1)
TextBox3.Text = ds.Info(2)
End Sub
Public Sub FillList()
For i = 0 To 2
ds.Info.Add("Hello")
Next
End Sub
End Class
Public Class DataStorage
Public Info As New List(Of String)
End Class

VB.net - How can a definition at module level throw an exception

I was beginning to think that I was getting good at VB.net, but not this one has me stumped.
Code looks something like this
Public Class MyServer
.....
Public myMQTTclient = New MqttClient("www.myserv.com")
.....
Private Sub Ruptela_Server(sender As Object, e As EventArgs) Handles
MyBase.Load
<some code>
StartMQTT()
<some more code>
MQTT_Publish(.....)
End Sub
Public Function StartMQTT()
' Establish a connection
Dim code As Byte
Try
code = myMQTTclient.Connect(MQTT_ClientID)
Catch ex As Exception
<error handling code>
End Try
Return code
End Function
Public Sub MQTT_Publish(ByVal DeviceID As String, ByVal Channel As String, ByVal ChannelType As String, ByVal Value As String, ByVal Unit As String)
Dim myTopic As String = "MyTopic"
Dim myPayload As String = "My Payload"
Dim msgId As UShort = myMQTTclient.Publish(myTopic, Encoding.UTF8.GetBytes(myPayload), MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE, False)
End Sub
As this stands it works 100% OK. The coding may seem a bit odd, but the intent is as follows :
a) create an object 'myMQTTclient' at module level so it has scope throughout the module
b) run StartMQTT() - It can still see the object.
c) within main program call MQTT_Publish many times - I can still see the object
Now the issue is this... it all goes well until "www.myserv.com" fails DNS, then the underlying winsock code throws an exception.
So ... I'm thinking - no problem - just wrap the declaration in a try block or check that www.myserv.com exists before launching the declaration.
Ah, but you can't put code at module level, it has to be in a sub or function.
Hmmm... now I'm stumped. There has to be a 'proper' way to do this, but I'll be darned if I can figure it out.
Anyone able to help ?
I'd follow the advice from #djv about declaring it just as you need it. To wrap that in a Try... Catch block you can do that in an Init method.
Public Class MyServer
Implements IDisposable ' As per djv recommendation which I second...
Private myMQQTclient As MqttClient
Public Sub Init()
Try
myMQQTClient = New MqttClient("<your url>")
Catch ex As Exception
' Do whatever
End Try
End Sub
' more code and implement the Dispose method...
End Class
You can then go on and implement the IDisposble interface to ensure that you release the resources.
Ok, I am not sure if I have the right library. But I found this Nuget package: OpenNETCF.MQTT which seems to have the class you are using.
I would do it this way
Public Class MyServerClass
Implements IDisposable
Public myMQTTclient As MQTTClient
'Private Sub Ruptela_Server(sender As Object, e As EventArgs) Handles MyBase.Load
' ' <some code>
' StartMQTT()
' ' <some more code>
' ' MQTT_Publish(.....)
'End Sub
Public Sub New(brokerHostName As String)
myMQTTclient = New MQTTClient(brokerHostName)
End Sub
Public Function StartMQTT()
' Establish a connection
Dim code As Byte
Try
code = myMQTTclient.Connect(MQTT_ClientID)
Catch ex As Exception
'<error handling code>
End Try
Return code
End Function
Public Sub MQTT_Publish(ByVal DeviceID As String, ByVal Channel As String, ByVal ChannelType As String, ByVal Value As String, ByVal Unit As String)
Dim myTopic As String = "MyTopic"
Dim myPayload As String = "My Payload"
Dim msgId As UShort = myMQTTclient.Publish(myTopic, Encoding.UTF8.GetBytes(myPayload), MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE, False)
End Sub
#Region "IDisposable Support"
Private disposedValue As Boolean
Protected Overridable Sub Dispose(disposing As Boolean)
If Not disposedValue Then
If disposing Then
myMQTTclient.Dispose()
End If
End If
disposedValue = True
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
End Sub
#End Region
End Class
And now you can see the usage when IDisposable is implemented, in a Using block:
Module Module1
Sub Main()
Using myserver As New MyServerClass("www.myserv.com")
myserver.StartMQTT()
myserver.MQTT_Publish(...)
End Using
End Sub
End Module
This makes it so your object is only in scope in the Using, and the object's Dispose method will automatically be called on End Using
I don't know what the base class was originally and why this was declared Private Sub Ruptela_Server(sender As Object, e As EventArgs) Handles MyBase.Load. It seems like it was possibly a form? You should keep your server code separate from Form code if that was the case. I suppose you could paste the Using into your form load, but then you would be blocking your UI thread. The referenced library has Async support so it might be a good idea to leverage that if coming from a UI.
I've made many assumptions, so I'll stop to let you comment and see how close relevant my answer is.

paypal API in VB.net

Hey all, i have converted some C# PayPal API Code over to VB.net. I have added that code to a class within my project but i can not seem to access it:
Imports System
Imports com.paypal.sdk.services
Imports com.paypal.sdk.profiles
Imports com.paypal.sdk.util
Namespace GenerateCodeNVP
Public Class GetTransactionDetails
Public Sub New()
End Sub
Public Function GetTransactionDetailsCode(ByVal transactionID As String) As String
Dim caller As New NVPCallerServices()
Dim profile As IAPIProfile = ProfileFactory.createSignatureAPIProfile()
profile.APIUsername = "xxx"
profile.APIPassword = "xxx"
profile.APISignature = "xxx"
profile.Environment = "sandbox"
caller.APIProfile = profile
Dim encoder As New NVPCodec()
encoder("VERSION") = "51.0"
encoder("METHOD") = "GetTransactionDetails"
encoder("TRANSACTIONID") = transactionID
Dim pStrrequestforNvp As String = encoder.Encode()
Dim pStresponsenvp As String = caller.[Call](pStrrequestforNvp)
Dim decoder As New NVPCodec()
decoder.Decode(pStresponsenvp)
Return decoder("ACK")
End Function
End Class
End Namespace
I am using this to access that class:
Private Sub cmdGetTransDetail_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdGetTransDetail.Click
Dim thereturn As String
thereturn =GetTransactionDetailsCode("test51322")
End Sub
But it keeps telling me:
Error 2 Name 'GetTransactionDetailsCode' is not declared.
I'm new at calling classes in VB.net so any help would be great! :o)
David
Solved
Dim payPalAPI As New GenerateCodeNVP.GetTransactionDetails
Dim theReturn As String
theReturn = payPalAPI.GetTransactionDetailsCode("test51322")
You're probably getting that error because you need to call it like this:
Private Sub cmdGetTransDetail_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdGetTransDetail.Click
Dim thereturn As String
Dim myTransaction as new GetTransactionDetails
thereturn = myTransaction.GetTransactionDetailsCode("test51322")
End Sub
Or you can make the function be a public shared function and access it as if it were a static method
Your GetTransactionDetailsCode method is an instance method of the GetTransactionDetails class, which means that you need an instance of the GetTransactionDetails class in order to call the method.
You can do that like this:
Dim instance As New GetTransactionDetails()
thereturn = instance.GetTransactionDetailsCode("test51322")
However, your method doesn't actually use the class instance, so you should change your GetTransactionDetails class to a Module instead.