Error with the Using Statement and Lazy Initialized Property - vb.net

The code below will throw an InvalidOperationException: The ConnectionString property has not been initialized. The exception is thrown at the line calling Connection.Open() in the Load method. If I use the try-finally statement instead of the using statement, everything works correctly. Can anyone explain why the exception occurs with the using statement?
Public Class SomeEntity
Private _Connection As SqlClient.SqlConnection
Private _ConnectionString As String
Protected ReadOnly Property Connection() As SqlClient.SqlConnection
Get
If _Connection Is Nothing Then
_Connection = New SqlClient.SqlConnection(_ConnectionString)
End If
Return _Connection
End Get
End Property
Public Sub New(ByVal connectionString As String)
_ConnectionString = connectionString
End Sub
Public Sub Load(ByVal key As Integer)
Using Connection
Connection.Open()
...
End Using
End Sub
End Class

You failed to mention a key piece of information: It succeeds the first time Load() is called, but then fails forever after.
When using Using, Dispose() is called on the used variable when the Using block exits. So in your scenario:
Load() gets called
The Using statement calls the Connection property's Get
_Connection gets set to a new SqlConnection and returned
The returned connection gets opened and used normally
The Using block exits, calling Dispose() on the connection
At this point, the SqlConnection object still exists, and is still pointed to by _Connection. It's no longer in a usable state though, since it's been Dispose()d. When the second call to Load() comes in:
Load() gets called
The Using statement calls the Connection property's Get
_Connection is still pointing to a (useless) SqlConnection object, therefore it's not Nothing, and doesn't get set to a new SqlConnection object
The (useless) connection gets returned
Open() gets called on the connection--which is in an unusable state--and triggers the InvalidOperationException
You're mixing conflicting approaches to connection management. Keeping the connection object around as a member of the class implies that you want to keep the connection alive for the life of the SomeEntity object, but using Using implies that you want to create and destroy the connection on the fly with each usage.

Connection in your Using statement is not being initialized or declared. Your code should read more like:
Public Sub Load(ByVal key As Integer)
Using myConnection as SqlClient.SqlConnection = Me.Connection
Connection.Open()
...
End Using
End Sub

Related

Pass an instance of a same class in new method and replace "me"

I'm sorry but it's difficult to explain my problem.
I have a class and in the new method, I want to be able to pass an instance of the class - if an instance is passed, this instance should be used (replace 'me').
An example...
Public Class DataConnection
Implements IDisposable
Public Property SqlCnn As SqlClient.SqlConnection
Private _isNewInstance As Boolean
Public Sub New()
_isNewInstance = True
SqlCnn = New SqlClient.SqlConnection
End Sub
Public Sub New(dc As DataConnection)
_isNewInstance = False
Me = dc ' <- this is not valid
End Sub
#Region "IDisposable Support"
Private disposedValue As Boolean
Protected Overridable Sub Dispose(disposing As Boolean)
If Not disposedValue Then
If disposing Then
End If
If _isNewInstance Then
SqlCnn.Close()
SqlCnn.Dispose()
SqlCnn = Nothing
End If
' Do my clean up code
End If
disposedValue = True
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
End Sub
#End Region
End Class
Then I want to be able to create new instances like this:
Dim dc As New DataConnection
Dim dc2 As New DataConnection(dc)
I need it to support "using" statement, since my real class implements iDisposable and I need to do one thing if no instance was passed and another thing is an instance was passed in the new method. So I can't use singleton.
Okay, where to start?
Me refers to the inside of the current object.
Such as a Form, Class, or other Object.
Me is shorthand and doesn't work like a pointer in C.
If you really want to copy properties of two classes, you should either implement a .Copy Sub or do it outside of the class.
Have you tried
Public Function CopyAnObject(ByVal Source as Object) as Object
CopyAnObject = Source
End Function
I'm confused what the use case for this could be, but seems like you're trying to hack something which isn't worth hacking?
Can the OP provide any more information on what s/he wants to achieve?
If you're really desperate to hack it, convert the whole Class into an IO.MemoryStream and hack it from there.

Calling a Sub that Connects to DB from Thread

I'm writing a VB.net 2017 Windows Service that looks at SQL and depending on the number of rows, it will create multiple threads. These threads monitor folders and report back to a different table and log the data accordingly. This piece of code has been running for a few years and has been working very well but in the last few days, I've decided to switch it from a console application that runs on start up to a window service and this is my first time writing a windows service.
I've went through and got the code to work but testing was a major pain because I couldn't walk through the code. I made some changes and consolidated some of the duplicate sections. For example, I was writing 4 or 5 different sections of code to either write data to SQL or pull data from SQL. I consolidated these down to only 2 sub routines and the threads continuously use them. Depending on the situation, the program can have between 1-15 threads and when I start activating more threads, I started to run into an issue. I already had try statements in my code from the console app before I ported it and I just put these into a log table when creating the new program and it was complaining that I was trying to Open an "Open connection". Here is an example of one the routines that pulls data from SQL:
Public con As New SqlClient.SqlConnection
Public dsGeneral As New DataSet
Public dc1 As SqlClient.SqlCommand
Public pullGeneral As SqlClient.SqlDataAdapter
Public maxRowsGeneral As Integer
Public Sub PullGeneralSQL(ByVal SQL As String)
Try
If (con.State = ConnectionState.Closed) Then
con.Open()
End If
dsGeneral.Tables.Clear()
pullGeneral = New SqlClient.SqlDataAdapter(SQL, con)
pullGeneral.Fill(dsGeneral, "General")
maxRowsGeneral = dsGeneral.Tables("General").Rows.Count
Catch ex As Exception
Msg(ex.Message, "Error")
maxRowsGeneral = 0
End Try
con.Close()
End Sub
I'm also getting errors saying that the connection is already closed as well. I'm assuming that another thread has finished connecting and closed the connection while a thread was in the middle of a task.
My question is, what is the best way to handle this?
I have run into this issue before even though there is connection pooling eventually as you open and close and open and close connections the database ends up throwing errors like you are getting. In my case it was 600 threads over and over. Worked for a little while then threw the same types of errors.
The solution for me was to create my own connection pool.
Public Class Form1
Public dsGeneral As New DataSet
Public dc1 As SqlClient.SqlCommand
Public pullGeneral As SqlClient.SqlDataAdapter
Public maxRowsGeneral As Integer
Public Sub PullGeneralSQL(ByVal SQL As String)
'Get a connection from the list..Thread safe
'Your thread will wait here until there is a connection to grab.
'Since your max is 15 the 16++ threads will all wait their turn
Dim con As SqlClient.SqlConnection = ConnectionManager.WaitForConnection()
Try
dsGeneral.Tables.Clear()
pullGeneral = New SqlClient.SqlDataAdapter(SQL, con)
pullGeneral.Fill(dsGeneral, "General")
maxRowsGeneral = dsGeneral.Tables("General").Rows.Count
Catch ex As Exception
Msg(ex.Message, "Error")
maxRowsGeneral = 0
End Try
'Put the connection back into the list
'Allows another thread to start
ConnectionManager.ReleaseConnection(con)
End Sub
End Class
Public Class ConnectionManager
Public Shared poolLimit As Integer = 15 'Maximum number of connections to open.
Public Shared connectionString As String = "PUT YOUR CONNECTION STRING HERE"
'Since this is static it will create 15 connections and add them to the list when the service starts
Private Shared ConnectionList As New IThreadPool(Of SqlClient.SqlConnection)(Function()
Dim connection As New SqlClient.SqlConnection(connectionString)
connection.Open()
Return connection
End Function, poolLimit)
''' <summary>
''' Gets the pool count.
''' Purely for information to get the number of connections left in the list
''' </summary>
Public ReadOnly Property PoolCount() As Integer
Get
Return ConnectionList.PoolCount
End Get
End Property
''' <summary>
''' Waits until there is a free connection in the list
''' When there is a free connection grab it and hold it
''' </summary>
Public Shared Function WaitForConnection() As SqlClient.SqlConnection
Try
Return ConnectionList.GetObject()
Catch ex As Exception
'only during close
Throw ex
End Try
Return Nothing
End Function
''' <summary>
''' Releases the connection.
''' Put the connection back into the list.
''' </summary>
Public Shared Sub ReleaseConnection(connection As SqlClient.SqlConnection)
Try
ConnectionList.PutObject(connection)
Catch ex As Exception
'only during close
Throw ex
End Try
End Sub
''' <summary>
''' Disposes this instance.
''' Make sure to dispose when the service shuts down or your connections will stay active.
''' </summary>
Public Shared Sub Dispose()
ConnectionList.Dispose()
End Sub
End Class
Public Class IThreadPool(Of T)
Private connections As System.Collections.Concurrent.BlockingCollection(Of T)
Private objectGenerator As Func(Of T)
Public Sub New(objectGenerator As Func(Of T), boundedCapacity As Integer)
If objectGenerator Is Nothing Then
Throw New ArgumentNullException("objectGenerator")
End If
connections = New System.Collections.Concurrent.BlockingCollection(Of T)(New System.Collections.Concurrent.ConcurrentBag(Of T)(), boundedCapacity)
Me.objectGenerator = objectGenerator
Task.Factory.StartNew(Function()
If connections.Count < boundedCapacity Then
Parallel.[For](0, boundedCapacity, Function(i)
Try
If connections.Count < boundedCapacity Then
connections.Add(Me.objectGenerator())
End If
Catch ex As Exception
'only error during close
End Try
End Function)
Try
While connections.Count < boundedCapacity
connections.Add(Me.objectGenerator())
End While
Catch ex As Exception
'only error during close
End Try
End If
End Function)
End Sub
Public ReadOnly Property PoolCount() As Integer
Get
Return If(connections IsNot Nothing, connections.Count, 0)
End Get
End Property
Public Function GetObject() As T
Return connections.Take()
End Function
Public Sub PutObject(item As T)
connections.Add(item)
End Sub
Public Sub Dispose()
connections.Dispose()
End Sub
End Class

Error when I add connection string in a UserControl

I am facing a weird problem, I am trying to create Usercontrol (Add_companies)and add it to panel
Everything work fine but when I add the connection to the user control I face this error :
The variable 'Add_companies1' is either undeclared or was never assigned.
And this is my connection:
Protected configuracion As ConnectionStringSettings = ConfigurationManager.ConnectionStrings("Conn")
Dim Conn As New SqlConnection(configuracion.ConnectionString)
Public Cmd As SqlCommand
Error one image
where the error code must be
and when i go to the code line error there's nothing.
Protected configuracion As ConnectionStringSettings = ConfigurationManager.ConnectionStrings("Conn")
Dim Conn As New SqlConnection(configuracion.ConnectionString)
Public Cmd As SqlCommand
You indicate that you placed this code in your UserControl and now the Form designer is throwing errors. The VS IDE is loading and running an instance of the usercontrol when it is placed on the design surface.
These three lines of code would have to be placed at the class level to support the Protected and Public access modifiers. The issue is that the designer is not able to retrieve ConfigurationManager.ConnectionStrings("Conn") so configuracion is null when it is used in Dim Conn As New SqlConnection(configuracion.ConnectionString).
When you use the syntax: Dim var as New Something() at the class level, the compiler converts that to Dim var as Something and places var = New Something() into the class constructor (Sub New) code.
To fix this problem you need to prevent code that will throw an error while in design mode from executing. All controls have a Boolean property named DesignMode for this purpose. Note that DesignMode will not be valid if used in the constructor code, so trying to use it there will fail.
Imports System.Configuration
Imports System.Data.SqlClient
Public Class Add_companies
Protected configuracion As ConnectionStringSettings
Private Conn As SqlConnection
Public Cmd As SqlCommand
Private Sub Add_companies_Load(sender As Object, e As EventArgs) Handles MyBase.Load
If Not Me.DesignMode Then
configuracion = ConfigurationManager.ConnectionStrings("Conn")
Conn = New SqlConnection(configuracion.ConnectionString)
End If
End Sub
End Class

Getting the infamous "The remote server returned an error: NotFound" error

guys. I'm pretty new to both Silverlight and WCF. What I'm trying to do is provide a database connection to my Silverlight object. The problem I'm having, though, is that my web service, while it compiles fine, doesn't seem to be returning anything at all. I've used Fiddler, and tried a custom request, but the service itself isn't returning anything at all.
My service code:
Imports System.ServiceModel
Imports System.ServiceModel.Activation
Imports System.Data.OleDb
Imports System.Runtime.Serialization
<ServiceContract(Namespace:="")> _
<AspNetCompatibilityRequirements(RequirementsMode := AspNetCompatibilityRequirementsMode.Allowed)> _
Public Class DBConnection
Dim dbConn As OleDbConnection
Private Function OpenConnection(ByVal strConnection As String) As Boolean
Try
Dim blnConnected As Boolean = False
dbConn = New OleDbConnection(strConnection)
dbConn.Open()
Catch ex As OleDbException
Return False
End Try
Return True
End Function
Private Function CloseConnection() As Boolean
Try
dbConn.Close()
dbConn = Nothing
Catch ex As OleDbException
Return False
End Try
Return True
End Function
<OperationContract()> _
Public Function GetDataTable(ByVal strSQL As String, ByVal strConnection As String) As DataTable
If dbConn Is Nothing Then OpenConnection(strConnection)
Dim dtTable As New DataTable
Dim dbAdapter As New OleDbDataAdapter(strSQL, dbConn)
dbAdapter.Fill(dtTable)
CloseConnection()
Return dtTable
End Function
End Class
When I breakpoint it on the last line of GetDataTable, there ARE results there. And I know it's hitting the service because of that, too.
So obviously, I'm doing something wrong, but I have absolutely no idea what.
You must be getting something in Fiddler if you see
“The remote server returned an error: NotFound” error.
Is there an HTTP error code at least?
----- Updated
Use DataSet instead of DataTable as the return type. Put the table inside of the DataSet before returning and grab the first Table.

VB.NET constructors

I'm doing a lab for school, and I came across something I have never done before: create a default constructor within my class. It involves creating a private field to store the connection string, and then create a default constructor that sets the connection string.
Here is what I have so far:
Public Class Appointments
Private sqlconnection As String = ConfigurationSettings.AppSettings("ConnectionString")
Private Property connectionstring() As String
Get
Return sqlconnection
End Get
Set(ByVal value As String)
End Set
End Property
Public Sub New(ByVal sConnectionString As String)
sqlconnection = sConnectionString
End Sub
Am I doing this right? What is going on?
Looks good to me but you've already initilized the connection string as a private variable up top.
Are you supposed to allow someone to pass in a connection string?
Your set may need to be:
Set(ByVal value as String)
sqlconnection = value
End Set
You also need a parameterless constructor, something which gives the string a value when you instantiate an object.
For instance the parameterless constructor could be set by a web / app config file:
public sub new()
sqlconnection = ConfigurationSettings.AppSettings("ConnectionString")
end sub
The whole thing could be:
Public Class Appointments
Private sqlconnection As String
Private Property connectionstring() As String
Get
Return sqlconnection
End Get
Set(ByVal value As String)
sqlconnection = value
End Set
End Property
Public Sub New()
sqlconnection = ConfigurationSettings.AppSettings("ConnectionString")
End Sub
'optional you could add this but not sure how much of a fuss your professor might make it
'parameterized constructor
Public Sub New(ByVal sConnectionString As String)
sqlconnection = sConnectionString
End Sub
End Class
A default constructor is a constructor which takes no arguments. In this case you've defined a constructor which takes 1 argument and sets it to a private field. You'll need to change your code in the following ways
Have the constructor take no parameters
Move the initialization of the private field into the constructor
Like so
Public Class Appointments
Private sqlconnection As String
...
Public Sub New()
sqlconnection = ConfigurationSettings.AppSettings("ConnectionString")
End Sub
End Class
Whether you are doing it right or not depends on what you want to do.
What you are doing is the following:
You declare a private String field called sqlconnection and you initialize it to contain a value from the config file:
Private sqlconnection As String = ConfigurationSettings.AppSettings("ConnectionString")
...then you have set up a property to expose the field:
Private Property connectionstring() As String
Get
Return sqlconnection
End Get
Set(ByVal value As String)
End Set
End Property
Noticable here is that you do not do anything in the Set accessor. If the property is to be readonly; remove the Set accessor and mark the property as ReadOnly. Also, the property is Private, effectively giving it the same scope as the field. Perhaps this is intentional, but if you want to be able to get (or set) the value of the property from outside the class, it should not be Private.
Finllaly, you define a constructor, taking a String parameter, the value of which is assigned to the private field:
Public Sub New(ByVal sConnectionString As String)
sqlconnection = sConnectionString
End Sub
This looks quite OK, even though it is a bit unnecessary to initialize the field, when you replace the value in the constructor. Since the class does not define a parameter-less constructor, there is no way to create it without replacing the value of sqlconnection.