vb.net 3 tier architecture error message open connection - sql

i work in vb.net project using 3 tier architecture
i have class BL include function name : show all customer and this is the code :`
Public Function ShowCustomer() As Customer()
Dim sql As String
Dim emps(-1) As Customer
Dim e1 As Customer
sql = "select Customer_name from Customer"
Dim dr As SqlDataReader = OBJECT_M.Exe_SQL(sql)
While dr.Read
e1 = New Customer
e1.Customer_Name = dr(0)
ReDim Preserve emps(UBound(emps) + 1)
emps(UBound(emps)) = e1
End While
Return emps
End Function
now in interface i need to handle all Customer name and write the name in the listview
my code:
Dim obj As New BL
Dim obj2 As New Customer
Dim x As Integer = 1
While x <> obj.ShowCustomer.Length
lb.Items.Add(obj.ShowCustomer(x).Customer_Name.ToString())
End While
but i have error message talk about this :
Additional information: The connection was not closed. The connection's current state is open.
please can Anybody save my day ?

For SqlConnection, SqlCommand, SqlDataReader, DataTable, DataSet (and any other disposable object) make sure to Dispose() of the object when you are done.
If you don't call Dispose() (or Close() in this case) connections will remain open and you will get memory and handle leaks.
The easist way is to use the Using keyword (which will also dispose in the case of an exception or other short circuiting event:
Using dr As SqlDataReader = OBJECT_M.Exe_SQL(sql)
While dr.Read
e1 = New Customer
e1.Customer_Name = dr(0)
ReDim Preserve emps(UBound(emps) + 1)
emps(UBound(emps)) = e1
End While
End Using
Make sure to do the same in the object I have detailed above in OBJECT_M.Exe_SQL, once you have called Dispose()/Close() on an oject you will not be able to use the instance of the object again and will have to create a new instance.
Refer to the documentation for more examples: https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader(v=vs.110).aspx
Also while there is nothing wrong with using arrays, you may find it easire to use a List(of T) which you can later convert to an using ToArray().

Related

vb.net Mock an "iterative database" connection inside the managed code

Problem: I am refactoring vb.net code to make it more unit test friendly. I have a vb.net function which has 2 data components. In the original code, both data models were implemented in the code which made it almost impossible to pass test data. I implemented an interface for the first data object which is now injected into the call to the function. This solves the issue of passing mock data to the function when testing. The second data component is executed iteratively inside the function.
Question - Is there a way to "mock" the iterative data retrieval while still maintaining the code functionality?
Basic Function - The data object injected at call time is used to pass parameters to the second database call within the code. This is repeated for each row in the injected DataTable object.
Function Signature: date and dailyScores are injected for Unit Tests or Run Time Execution
Public Shared Function CalcDailyScoresByDate(ByVal dayUtc As Date,
ByVal ReID As Integer,
ByVal dailyScores As DataTable
) As DailyDataCollection
Function Code:
Public Shared Function CalcDailyScoresByDate(ByVal dayUtc As Date,
ByVal ReID As Integer,
ByVal dailyScores As DataTable
) As DailyDataCollection
Dim timeUtc As Date = dayUtc
Dim srvSet As ServiceSettings = New ServiceSettings(dayUtc)
Dim ept As Date = TimeZoneInfo.ConvertTimeFromUtc(timeUtc, TimeZoneInfo.Local)
ept = ept.AddDays(1)
Dim effDayUtc As Date = TimeZoneInfo.ConvertTimeToUtc(ept)
Dim daily As New DailyDataCollection
Try
#Region "Region DataAccess"
Dim regDs = New DataAccess
Try
regDs.InitializeFromMDB(srvSet.MyConnectionString, "MyDb")
Catch ex As Exception
MyUtil.ErrorLog("RegDS.InitializeFromMDB " & ex.Message)
End Try
For Each rw In dailyScores.Rows
Dim drw As New DailyData
drw.ReID = rw(0)
drw.SignalID = rw(1)
drw.MyDay = effDayUtc
drw.dailyScore = rw(2)
' Calls the actual implementation to retrieve the supporting data for each record in the outer for loop
' Need to find a way to allow this to be Unit Tested with Mock data
Dim rsc = regDs.GetRegResource(drw.ReID, drw.SignalID, effDayUtc)
If rsc.Type = DataAccess.ReTypes.DG Then
For Each child In rsc.Children
Dim childDaily As New DailyData
childDaily.ReID = child.ID
childDaily.SignalID = child.SignalID
childDaily.myDay = effDayUtc
childDaily.dailyScore = drw.dailyScore
daily.Add(childDaily)
Next
End If
Next
dailyScores.Dispose()
dailyScores = Nothing
srvSet = Nothing
#End Region
Catch ex As Exception
MyUtil.ErrorLog("CalcDailyScoresByDate: " & ex.Message)
End Try
Return daily
End Function

How can i resolve ExecucuteNonQuery throwing exception Incorrect syntex near '?'

Dim StrSql = "update student set id=?"
Updated (StrSql,15)
Public Function Updated (ByVal strSql As String, ByVal ParamArray Parameters As String ())
For Each x In Parameters
cmd.Parameters.AddWithValue("?",x)
Next
cmd.ExecuteNonQuery()
End Function
You didn't leave us much to go on; as jmcilhinney points out, you need to add more detail to future questions. For example in this one you have code there that doesn't compile at all, doesnt mention the types of any variable, you don't give the name of the database...
...I'm fairly sure that "Incorrect syntax near" is a SQL Server thing, in which case you need to remember that it (re)uses named parameters, unlike e.g. Access which uses positional ones:
SQL Server:
strSql = "SELECT * FROM person WHERE firstname = #name OR lastname = #name"
...Parameters.AddWithValue("#name", "Lee")
Access:
strSql = "SELECT * FROM person WHERE firstname = ? OR lastname = ?"
...Parameters.AddWithValue("anythingdoesntmatterwillbeignored", "Lee")
...Parameters.AddWithValue("anythingdoesntmatterwillbeignoredalso", "Lee")
This does mean your function will need to get a bit more intelligent; perhaps pass a ParamArray of KeyValuePair(Of String, Object)
Or perhaps you should stop doing this way right now, and switch to using Dapper. Dapper takes your query, applies your parameters and returns you objects if you ask for them:
Using connection as New SqlConnection(...)
Dim p as List(Of Person) = Await connection.QueryAsync(Of Person)( _
"SELECT * FROM person WHERE name = #name", _
New With { .name = "John" } _
)
' use your list of Person objects
End Using
Yep, all that adding parameters BS, and executing the reader, and converting the results to a Person.. Dapper does it all. Nonquery are done like connection.ExecuteAsync("UPDATE person SET name=#n, age=#a WHERE id=#id", New With{ .n="john", .a=27, .id=123 })
http://dapper-tutorial.net
Please turn on Option Strict. This is a 2 part process. First for the current project - In Solution Explorer double click My Project. Choose Compile on the left. In the Option Strict drop-down select ON. Second for future projects - Go to the Tools Menu -> Options -> Projects and Solutions -> VB Defaults. In the Option Strict drop-down select ON. This will save you from bugs at runtime.
Updated(StrSql, 15)
Your Updated Function calls for a String array. 15 is not a string array.
Functions need a datatype for the return.
cmd.Parameters.AddWithValue("?", X)
cmd is not declared.
You can't possible get the error you mention with the above code. It will not even compile, let alone run and produce an error.
It is not very helpful to write a Function that is trying to be generic but is actually very limited.
Let us start with your Update statement.
Dim StrSql = "update student set id=?"
The statement you provided will update every id in the student table to 15. Is that what you intended to do? ID fields are rarely changed. They are meant to uniquely identify a record. Often, they are auto-number fields. An Update command would use an ID field to identify which record to update.
Don't use .AddWithValue. See http://www.dbdelta.com/addwithvalue-is-evil/
and
https://blogs.msmvps.com/jcoehoorn/blog/2014/05/12/can-we-stop-using-addwithvalue-already/
and another one:
https://dba.stackexchange.com/questions/195937/addwithvalue-performance-and-plan-cache-implications
Here is another
https://andrevdm.blogspot.com/2010/12/parameterised-queriesdont-use.html
Since you didn't tell us what database you are using I guessed it was Access because of the question mark. If it is another database change the connection, command and dbType types.
Using...End Using block ensures you connection and command are closed and disposed even if there is an error.
Private ConStr As String = "Your Connection String"
Public Function Updated(StudentNickname As String, StudentID As Integer) As Integer
Dim RetVal As Integer
Using cn As New OleDbConnection(ConStr),
cmd As New OleDbCommand("Update student set NickName = #NickName Where StudentID = #ID", cn)
cmd.Parameters.Add("#NickName", OleDbType.VarChar, 100).Value = StudentNickname
cmd.Parameters.Add("#ID", OleDbType.Integer).Value = StudentID
cn.Open()
RetVal = cmd.ExecuteNonQuery
End Using
Return RetVal
End Function
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim RowsUpdated = Updated("Jim", 15)
Dim message As String
If RowsUpdated = 1 Then
message = "Success"
Else
message = "Failure"
End If
MessageBox.Show(message)
End Sub
This code keeps your database code separated from user interface code.

Database Lookup From ComboBox selection

I have a question about database values and how to determine the id of a value that has been changed by the user at some point.
As it is currently set up there is a combobox that is populated from a dataset, and subsequent text boxes whose text should be determined by the value chosen from that combobox.
So let's say for example you select 'Company A' from the combobox, I would like all the corresponding information from that company's row in the dataset to fill the textboxes (Name = Company A, Address = 123 ABC St., etc.,)
I am able to populate the combobox just fine. It is only however when I change the index of the combobox that this specific error occurs:
An unhandled exception of type 'System.Data.OleDb.OleDbException'
occurred in System.Data.dll
Additional information: Data type mismatch in criteria expression.
Here is the corresponding code:
Imports System.Data.OleDb
Public Class CustomerContact
Dim cn As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|datadirectory|\CentralDatabase.accdb;")
Dim da As New OleDbDataAdapter()
Dim dt As New DataTable()
Private Sub CustomerContact_Load(sender As Object, e As EventArgs) Handles MyBase.Load
cn.Open()
da.SelectCommand = New OleDbCommand("select * from Customers", cn)
da.Fill(dt)
Dim r As DataRow
For Each r In dt.Rows
cboVendorName.Items.Add(r("Name").ToString)
cboVendorName.ValueMember = "ID"
Next
cn.Close()
End Sub
Private Sub cboVendorName_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cboVendorName.SelectedIndexChanged
cn.Open()
da.SelectCommand = New OleDbCommand("select * from Customers WHERE id='" & cboVendorName.SelectedValue & "'", cn)
da.Fill(dt)
Dim r As DataRow
For Each r In dt.Rows
txtNewName.Text = "Name"
txtAddress.Text = "Address"
Next
cn.Close()
End Sub
The error is caught at Line 24 of this code, at the second da.Fill(dt) . Now obviously from the exception I know that I am sending in a wrong datatype into the OleDbCommand, unfortunately I am a novice when it comes to SQL commands such as this. Also please keep in mind that I can't even test the second For loop, the one that is supposed to fill the Customer information into textboxes (for convenience I only copied the first two textboxes, of which there are nine in total). I am think I could use an If statement to determine if the row has been read, and from there populate the textboxes, but I will jump that hurdle when I can reach it.
Any guidance or suggestions would be much appreciated. Again I am a novice at managing a database and the code in question pertains to the project my current internship is having me write for them.
Since you already have all the data from that table in a DataTable, you dont need to run a query at all. Setup in form load (if you must):
' form level object:
Private ignore As Boolean
Private dtCust As New DataTable
...
Dim SQL As String = "SELECT Id, Name, Address, City FROM Customer"
Using dbcon = GetACEConnection()
Using cmd As New OleDbCommand(SQL, dbcon)
dbcon.Open()
dtCust.Load(cmd.ExecuteReader)
End Using
End Using
' pk required for Rows.Find
ignore = True
dtCust.PrimaryKey = New DataColumn() {dtCust.Columns(0)}
cboCust.DataSource = dtCust
cboCust.DisplayMember = "Name"
cboCust.ValueMember = "Id"
ignore = False
The ignore flag will allow you to ignore the first change that fires as a result of the DataSource being set. This will fire before the Display and Value members are set.
Preliminary issues/changes:
Connections are meant to be created, used and disposed of. This is slightly less true of Access, but still a good practice. Rather than connection strings everywhere, the GetACEConnection method creates them for me. The code is in this answer.
In the interest of economy, rather than a DataAdapter just to fill the table, I used a reader
The Using statements create and dispose of the Command object as well. Generally, if an object has a Dispose method, put it in a Using block.
I spelled out the columns for the SQL. If you don't need all the columns, dont ask for them all. Specifying them also allows me to control the order (display order in a DGV, reference columns by index - dr(1) = ... - among other things).
The important thing is that rather than adding items to the cbo, I used that DataTable as the DataSource for the combo. ValueMember doesn't do anything without a DataSource - which is the core problem you had. There was no DataSource, so SelectedValue was always Nothing in the event.
Then in SelectedValueChanged event:
Private Sub cboCust_SelectedValueChanged(sender As Object,
e As EventArgs) Handles cboCust.SelectedValueChanged
' ignore changes during form load:
If ignore Then Exit Sub
Dim custId = Convert.ToInt32(cboCust.SelectedValue)
Dim dr = dtCust.Rows.Find(custId)
Console.WriteLine(dr("Id"))
Console.WriteLine(dr("Name"))
Console.WriteLine(dr("Address"))
End Sub
Using the selected value, I find the related row in the DataTable. Find returns that DataRow (or Nothing) so I can access all the other information. Result:
4
Gautier
sdfsdfsdf
Another alternative would be:
Dim rows = dtCust.Select(String.Format("Id={0}", custId))
This would return an array of DataRow matching the criteria. The String.Format is useful when the target column is text. This method would not require the PK definition above:
Dim rows = dtCust.Select(String.Format("Name='{0}'", searchText))
For more information see:
Using Statement
Connection Pooling
GetConnection() method aka GetACEConnection

WCF EF return as list

Hi I got the error when return EF as the list. Here are my codes.
WCF
Public Function GetMerchantList() As List(Of Merchant) Implements IMerchant.GetMerchantList
Dim ws As New aMerchantService.MerchantServiceClient
Dim General As New General
Dim kWSUrl As String = ""
Dim endpointAddress = ws.Endpoint.Address
Dim newEndpointAddress As New EndpointAddressBuilder(endpointAddress)
kWSUrl = General.ConvertWsURL("App")
newEndpointAddress.Uri = New Uri(kWSUrl & "MerchantService.svc")
ws = New aMerchantService.MerchantServiceClient("BasicHttpBinding_IMerchantService", newEndpointAddress.ToEndpointAddress())
Dim Data = ws.GetMerchantList()
Return Data
End Function
Merchant Class
Public Function GetMerchantList() As List(Of Merchant)
Dim Db As New TTMSEntities
Dim Data = (From p In Db.TT_MERCHANT Join r In Db.TT_BRANCH_SETTING On _
p.MERCHANT_BRANCH_INTERNAL_NUM Equals r.INTERNAL_NUM _
Select New Merchant With {.MerchantID = p.MERCHANT_ID,
.MerchantName = p.DESCRIPTION,
.BranchID = r.INTERNAL_NUM,
.BranchName = r.BRANCH_DESC})
If Data IsNot Nothing Then
Return Data.ToList
Else
Return Nothing
End If
End Function
The error is Error Value of type '1-dimensional array of
TTMS.App.WebSites.Data.Merchant' cannot be converted to
'System.Collections.Generic.List(Of TTMS.Web.WebSites.WCF.Merchant)'.
Please help. Thanks
It looks like you're using a service reference. By default, WCF will serialize generic lists as arrays. To override this behavior, when you go to add the service reference, click on the Advanced button at the bottom left corner. This will bring up the Service Reference Settings. Select System.Generics.List for the collection type (the default is System.Array):

calling function to be used on windows form

this is in my OfficeEquipment.Frm
Public Function Loadfunction()
dt = Functions.LoadData()
End Function
and this is the error for the above code
Warning 1 Function 'Loadfunction' doesn't return a value on all code paths. A null reference exception could occur at run time when the result is used. C:\Documents and Settings\IJDinglasan\My Documents\Visual Studio 2008\Projects\Electronic Office Equipment History\Electronic Office Equipment History\Update Office Equipment Profile.vb 9 5 Electronic Office Equipment History
this is in my module Functions
Private Function LoadData() As DataTable
Using sqlconn = New SqlClient.SqlConnection("server = SKPI-APPS1;" & _
"Database = EOEMS;integrated security=true")
Dim dt As New DataTable
sqlconn.Open()
Dim da As New SqlDataAdapter("SELECT * FROM tblOfficeEquipmentProfile", sqlconn)
da.Fill(dt)
Return dt
End Using
End Function
Basically just use the class and the function you have....
Dim dt As DataTable
dt = yourclass.LoadData()
Now dt is your table that you can use where you like.
MrCoDeXeR
EDITS
Where ever you have your function (LoadData) you need to reference that class and the function. For example: say my main class is: frmMain.vb and my class that has my function is: frmStudents I want to get that function.
So.... on frmMain.vb you need to declare another DataTable and assign it.... see below...
Dim dt As DataTable
dt = frmStudents.LoadData()
We call the function from frmStudents and fill our new table with our data. What you need to do is set a breakpoint on: dt = frmStudents.LoadData() and run your solution. When you get to that line press F-11 and see if it jumps over to you other class that has your function. then press F-11 and continue to step-through and see if it throws an error. On your: Return dt in your function, hover over this if it makes it this far and click the magnifying glass and see if data exists or has your column names, if so your good to go...