Opening/closing sql connection - redundant code - sql

I was wondering what is the most basic way to avoid the following.
con.ConnectionString = connection_String
con.Open()
cmd.Connection = con
'database interaction here
cmd.Close()
I keep making those lines all over in my project, but I figure there has to be a better way to save on typing this over and over. It makes the code look even more sloppy than it already is!
Ended up with this, works well for me. Thanks for the help :)
Public Sub connectionState()
If con.State = 0 Then
con.ConnectionString = connection_String
con.Open()
cmd.Connection = con
Else
con.Close()
End If
End Sub

This is where a lot of programmers are tempted to create a "database layer" with a variations on method signatures that look like this:
Public DataSet ExecuteSQL(ByVal sql As String) As DataSet
That allows you to isolate all that boilerplate connection code in one place. An sql command string goes in, and data comes out. Easy.
Don't do it!
This is headed in the right direction, but has one very big flaw: it forces you to use string manipulation to substitute parameter values into your sql queries. That leads to horrible sql injection security vulnerabilities.
Instead, make sure you include some mechanism in your methods to prompt for the sql parameters separately. This usually comes in the form of an additional argument to the function, and could be as simple as an array of KeyValuePairs. If you're comfortable with lambdas, my preferred pattern looks like this:
Public Iterator Function GetData(Of T)(ByVal sql As String, ByVal addParameters As Action(Of SqlParameterCollection), ByVal translate As Func(Of IDatarecord, T)) As IEnumerable(Of T)
Using cn As New SqlConnection("connection string"), _
cmd As New SqlCommand(sql, cn)
addParameters(cmd.Parameters)
cn.Open()
Using rdr As SqlDataReader = cmd.ExecuteReader()
While rdr.Read()
Yield(translate(rdr))
End While
End Using
End Using
End Function
To call that function, you would do something like this:
Dim bigCustomers = GetData("SELECT * FROM Customers WHERE SalesTotal > #MinSalesTotal", _
Sub(p) p.Add("#MinSalesTotal", SqlDbType.Decimal, 1000000), _
MyCustomerClass.FromIDataRecord)

You can try creating a class ( a singleton class ), and write the database connection syntax code and exceptions in that class, then call one object to the main class to create the database connection, that's the best way in performance and keep writing the same code on and on...

You can use just using block, using execute dispose on non managed object in the end of treatment.
Link : http://msdn.microsoft.com/en-us/library/htd05whh(v=vs.80).aspx

Related

Data retrieval from Access file into DataTable not working

I have some code to connect a database with the program, but for some reason at run time it does not show the data from the DB.
Public Class Form2
Dim con As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\employee\employee.accdb")
Dim cmd As New OleDbCommand("", con)
Dim empDA As New OleDbDataAdapter
Dim empTable As New DataTable
Dim dr As OleDbDataReader
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.EmpTableAdapter.Fill(Me.EmpDataSet.emp)
Dim cmd As New OleDbCommand("select * from emp", con)
empDA = New OleDbDataAdapter("select * from emp", con)
empDA.Fill(empTable)
DataGridView1.DataSource = empDA
End Sub
The code in the question looks to be a bit muddled as to what needs to be done.
Variables should be limited to the minimum scope that they are needed in, and some things need to be disposed of after use (to avoid memory leaks, files remaining locked, and other problems with computer resources).
For the disposal, the Using statement is useful as it makes sure that that is done automatically.
You should try to put each logical piece of code in a suitably small method so that it is easier to work with. Perhaps something like this:
Imports System.Data.OleDb
Public Class Form2
Dim connStr As String = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\temp\employee.accdb"
Sub ShowEmployeeData()
Dim sql = "SELECT [Id], [FirstName] FROM [Employees] ORDER BY [Id]"
Using conn As New OleDbConnection(connStr)
Using cmd As New OleDbCommand(sql, conn)
Dim employees As New DataTable()
Dim da As New OleDbDataAdapter(cmd)
da.Fill(employees)
DataGridView1.DataSource = employees
End Using
End Using
End Sub
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
ShowEmployeeData()
End Sub
End Class
So the connStr variable is available everywhere in the class Form2, and the code that shows the employee data is in its own Sub.
When querying a database, you should specify the actual columns that you need, so that the columns are returned in the order you want, and order by one of the columns so that the data is returned in a predicatable order - databases are free to give you any order for anything if you don't tell them otherwise.
Ok, while you have some tips and suggestions, I'll give a few more.
First, try using the project setting to create the connection. The reason for this is not only can you create + test + make a connection, you can do this without code.
so, try this to build + create the connection:
so now click on the [...] and it will launch the connection builder for you!
So you now get this:
(use the change button if it defaulted to sql server).
So, using the above allows you to build ONE connection - one that you can use for the WHOLE application.
And there is the handy test connection button!!
So, in above we called the connection "MyDB".
That way we don't have messy connection strings in code, and we have ONE place to change/set the connection.
Now, in code?
Well, as a really nice tip?
You often need a connection
You often need a data reader.
and you need some sql (command text).
So, in place of declaring that reader, declaring the conneciton, and all that jazz?
use a command object!
Why?
because the command object has ALL of the above 3 objects in one nice simple ONE object.
As a result, you don't have to declare the 3 separate objects.
Just use and adopt a command object in MOST cases.
So, now our code has this:
Imports System.Data.OleDb
Public Class DataGridTest1
Private Sub DataGridTest1_Load(sender As Object, e As EventArgs) Handles Me.Load
Using cmdSQL As New OleDbCommand("SELECT ID, FirstName, LastName, HotelName from tblHotels",
New OleDbConnection(My.Settings.MyDB))
cmdSQL.Connection.Open()
Dim rstData As New DataTable
rstData.Load(cmdSQL.ExecuteReader)
DataGridView1.DataSource = rstData
End Using
End Sub
End Class
Note how simple - and how few variables we have to setup and declare.
So, I find this becomes quite much as easy as using Access or even writing older VB6 code.
So, try the above - its very little code, and use the connection builder in the "settings" for the application - thus removing the need to introduce connection strings all over the place in code.

List contains duplicate Persons

Please see the code below:
Public Function ExecuteDynamicQuery(ByVal strSQL As String, ByVal list As List(Of clsType), ByVal tyType As clsType) As List(Of clsType) Implements IGenie.ExecuteDynamicQuery
Dim objParameterValues As New clsParameterValues
Dim iConnectionBLL As iConnectionBLL
Dim objCon As DbConnection
Dim objDR As DbDataReader
Dim paramValues() As DbParameter
objParameterValues = New clsParameterValues
iConnectionBLL = New clsConnectionBLL()
objCon = iConnectionBLL.getDatabaseTypeByDescription("Genie2")
Using objCon
paramValues = objParameterValues.getParameterValues
objDR = clsDatabaseHelper.ExecuteReader(objCon, CommandType.Text, strSQL, paramValues)
Do While objDR.Read
Dim tyType2 As clsType = tyType
tyType.PopulateDataReader(objDR)
list.Add(tyType2)
Loop
objDR.Close()
Return list
End Using
End Function
An SQL statement is passed to the function along with clsType (the base type). A list of types is returned e.g. a list of Persons. For example, in this case strSQL = "SELECT * FROM Persons". A list of 500 persons is returned but they are all the same person (the last person added to the list). I realise this is because the list is referncing the same object for each entry. How do I change this?
This is a situation where making the method generic would be useful. For instance:
Public Function MyGenericMethod(Of T As New)() As List(Of T)
Dim results As New List(Of T)()
For i As Integer = 0 To 9
Dim item As New T()
' Populate item ...
results.Add(item)
Next
Return results
End Function
For what it's worth, though, I see people trying do this kind of thing often, and it never sits well with me. I'm always the first one in line to suggest that common code should be encapsulated and not duplicated all over the place, but, I've never been convinced that creating some sort of data access layer that encapsulates the calls to ADO, but doesn't also encapsulate the SQL, is a good idea.
Consider for a moment that ADO, is in-and-of-itself an encapsulation of that part of the data-access layer. Sure, it can take a few more lines of code than you might like to execute a simple SQL command, but that extra complexity is there for a reason. It's necessary in order to support all of the features of the data source. If you try to simplify it, inevitably, you will one day need to use some other feature of the data source, but it won't be supported by your simplified interface. In my opinion, each data access method should use all of the necessary ADO objects directly rather than trying to some how create some common methods to do that. Yes, that does mean that many of your data access methods will be very similar in structure, but I think you'll be happier in the long run.
I've reduced your original code. The following sample is functionally equivalent to what you posted. Without knowing more about your types, it will hard to give you anything more than this, but maybe the reduction will make the code clear enough for you to spot a solution:
Public Function ExecuteDynamicQuery(ByVal sql As String, ByVal list As List(Of clsType), ByVal type As clsType) As List(Of clsType) Implements IGenie.ExecuteDynamicQuery
Dim paramValues() As DbParameter = New clsParameterValues().getParameterValues()
Using conn As DbConnection = iConnectionBLL.getDatabaseTypeByDescription("Genie2"), _
rdr As DbDataReader = clsDatabaseHelper.ExecuteReader(conn, CommandType.Text, sql, paramValues)
While rdr.Read()
type.PopulateDataReader(rdr)
list.Add(type)
End While
Return list
End Using
End Function
There are a few additional bits of advice I can give you:
You must have some way to accept parameter information for your query that is separate from the query itself. The ExecuteReader() method that you call supports this, but you only ever pass it an empty array. Fix this, or you will get hacked.
A implementation that uses Generics (as posted in another answer) would be much simpler and cleaner. The Genie interface you're relying doesn't seem to be adding much value. You'll likely do better starting over with a system that understands generics.
The problem of re-using the same object over and over can be fixed by creating a new object inside the loop. As written, the only way to do that is with a New clsType (and it seems you may have Option Strict Off, such that this could blow up on you at run time), through some messy reflection code, a switch to using generics as suggested in #2, or a by accepting a Func(Of clsType) delegate that can build the new object for you.

Is this late binding or what?

I store encrypted email addresses in my Database and use the DoDecrypt Function of mine, to display the unencrypted email addresses in a AspGrid
So my LINQ query is something like
Dim Query = From c In DB.Something Select New With {.Email = DoDecrypt(c.Email)}
Which returns a set of the emails in debug mode...
After using a Linq to datatable function the results returned are the ones found in the Database, which are the encrypted ones.
What is going wrong here?
How should i modify my Linq query?
And here is the Linq to datatable function
Public Shared Function ToDataTable(DB As System.Data.Linq.DataContext, query As Object) As DataTable
If query Is Nothing Then
Throw New ArgumentNullException("query")
End If
Dim cmd As IDbCommand = DB.GetCommand(TryCast(query, IQueryable))
Dim adapter As New SqlDataAdapter()
adapter.SelectCommand = DirectCast(cmd, SqlCommand)
Dim dt As New DataTable("sd")
Try
cmd.Connection.Open()
adapter.FillSchema(dt, SchemaType.Source)
adapter.Fill(dt)
Finally
cmd.Connection.Close()
End Try
Return dt
End Function
You need to actually resolve the query, it is late bound by default, but if you're actually wanting to resolve the query, you need to just call .ToList(). I've just wrapped your query in brackets below and called .ToList() at the end. Not sure if this is the exact VB syntax (going from C# knowledge here), but the principal is still the same.
Dim Query = (From c In DB.Something Select New With {.Email = DoDecrypt(c.Email)})
.ToList()

Closing Oracle connection in VB.NET

In my program I got multiple spots where I open a Connection to an Oracle database, read some lines via a stored procedure which returns me a cursor, put them in an IDataReader, close the connection and go on.
Now everything works fine till the point of closing the connection. I've watched the connections opened to the database via TOAD for Oracle and I figured out, that the database seems to keep an connection opened after I said m_Connection.Close and m_Connection.Dispose.
I use Oracle.DataAccess and get an error after a short time, that there are to many connections. I debuged my session and made sure, that vb performs the close() command and it does it, but the database not. Anyone got an idea how to fix this?
In .Net, you need to wrap database connections in a Try/Catch/Finally block, and always close the connections in the Finally section. There is a shorthand for this called a Using block. This means keeping a connection as member of a class (as you seem to be doing) is almost always the wrong way to go about it. .Net is optimized so that it's better to create a new connection and command object for each query.
DataReaders are a little special: if you return a datareader out of using block, the connection can be closed before your DataReader is done with it. In C#, I normally get around the problem with an iterator (yield return). Since VB.Net lacks support for this construct, I might use an Action(Of IDataRecord) instead, like so:
Public Sub MyOracleQuery(ByVal id As Integer, ByVal ProcessRecord As Action(Of IDataRecord))
Dim sql As String = "SELECT <columns> FROM MyTable WHERE ID= #Id"
Using cn As New OracleConnection("connection string"), _
cmd As New OracleCommand(sql, cn)
cmd.Parameters.Add("#Id", SqlDbTypes.Int).Value = id
cn.Open()
Using (rdr As IDataReader = cmd.ExecuteReader())
While rdr.Read()
ProcessRecord(rdr)
End While
End Using
End Using
End Sub
You can now pass an anonymous method to this code when you call it:
Dim id As Integer
If Integer.TryParse(IDTextBox.Text, id) Then
MyOracleQuery(id, _
Function(r)
''#... Do something with each "r" here
End Function _
)
Else
''# Complain to user about invalid ID
End If
Note that this requires Visual Studio 2010 /.Net 4 for the mutli-line anonymous method. For older platforms, you'll want to declare the method.

Returning a DataTable in WCF and .NET

Thanks William, that was the ticket. Had to assign the name property on both ends [DataTable.TableName].
On a side note here: There appears to be some school of thought (no offense Marc) that the following statement is always true:
"Everything in the world can and should be made into an object."
It is, simply, not always true. There are cases where you cannot cram your 'object' into any cookie-cutter or class no matter how you try to customize it. For me to objectize this beast, I'd have to create roughly 4,000 objects. I don't have time to do that, and yet this project must run as a service. Frankly I think the developers at MickeySoft need to get out more into the real world, and see for themselves that although theory is great it does not represent true-life challenges. I am all for working with objects for the obvious benefits, but there is a reality that universals do not exist. In my trade, even 'most of the time' rarely happens.
Before they push out a new technology set, and cease support of an old one, they need to make sure that the new technology has the same capabilities the old one had.
For the record: The people who believe the above statement to be true are also the same people who would refuse to take the project I'm currently working on.
Just the same -- thank you both for your time, efforts and opinions!
I'm trying to create a WCF function that will return a table to my console testing app. I am a total noob. The data is 2-dimensional and looks like this:
23 Letter
42 Another Letter
43 Document
...
Here's what I'm trying to do:
<ServiceContract()> _
Public Interface ILetterWriter
<OperationContract()> _
Function GetLetter(ByVal LetterID As String, ByVal StateID As String, ByVal CompID As String, ByVal tblVar As DataTable) As String
<OperationContract()> _
Function GetLetterNames(ByVal DepartmentType As Integer) As DataTable
End Interface
Public Function GetLetterNames(ByVal DepartmentType As Integer) As DataTable Implements ILetterWriter.GetLetterNames
Dim SQLCon As New SqlClient.SqlConnection
Dim SQLCmd As New SqlClient.SqlCommand
'Connect to the database
SQLCon.ConnectionString = "Data Source=VMSQL08-SRV1;Initial Catalog=DotNetDev;User ID=aiis_pgmr;Password=ag58102;"
SQLCon.Open()
'Grab the stored procedure, which returns the letter names
SQLCmd.CommandText = "sp_GetLetters"
SQLCmd.CommandType = CommandType.StoredProcedure
SQLCmd.Connection = SQLCon
'Pass the parameters
SQLCmd.Parameters.AddWithValue("#LetterType", DepartmentType)
'Execute the stored procedure, fill the datatable from a data adapter
GetLetterNames = New DataTable
GetLetterNames.Load(SQLCmd.ExecuteReader)
'Shut it down
SQLCmd.Dispose()
SQLCon.Close()
SQLCon.Dispose()
End Function
...Of course, it won't work. I just need to get the WCF to pass a basic table to my console application. The execute SQL seems to work just fine, I just can't get the data back to my application.
Any help would be greatly appreciated.
Thanks,
Jason
I agree with the other poster.
However, if you are returning a DataTable, you have to set the "Name" property of the DataTable if you want to return it from a WCF Service.