i want to factorize all the calls to ado.net present in my web application to not repeat over and over the connection string and the open/close methods. I succeed to do it for the calls without parameter, but i need help for the ones with parameters.
For example, I had :
Dim strConnexion As String = "myConnectionString"
Dim strRequete As String = "DELETE FROM tbl_devis WHERE id_devis = " + TBDevis.Text
Dim oConnection As New SqlConnection(strConnexion)
Dim oCommand As New SqlCommand(strRequete, oConnection)
oConnection.Open()
oConnection.ExecuteNonQuery()
oConnection.Close()
I factorized it into :
ExecuteRequest("DELETE FROM tbl_devis WHERE id_devis = " + TBDevis.Text)
And the code of ExecuteRequest :
Public Shared Sub ExecuteRequest(ByVal strRequest As String)
Dim strConnection As String = ChaineDeConnexion()
Using objConnection = New SqlConnection(strConnection)
Dim objCommand As SqlCommand
objCommand = New SqlCommand(strRequest, objConnection)
objCommand.Connection.Open()
objCommand.ExecuteNonQuery()
End Using
End Sub
But I would like be able to pass to Execute request a collection of parameters. This is a very simple example of what kind of code I want to factorize :
Dim strConnexion As String = "myConnectionString"
Dim strRequete As String = "DELETE FROM tbl_devis WHERE id_devis = #id_devis"
Dim oConnection As New SqlConnection(strConnexion)
Dim oCommand As New SqlCommand(strRequete, oConnection)
With (myCommand.Parameters)
.Add(New SqlParameter("#id_devis", SqlDbType.Int))
End With
With myCommand
.Parameters("#id_devis").Value = TBDevis.Text
End With
oConnection.Open()
oConnection.ExecuteNonQuery()
oConnection.Close()
I was thinking about edit my ExecuteRequest function to add an optional parameters collection :
Public Shared Sub ExecuteRequest(ByVal strRequest As String, Optional ByRef sqlParameters As SqlParameterCollection = Nothing)
Dim strConnection As String = ChaineDeConnexion()
Using objConnection = New SqlConnection(strConnection)
Dim objCommand As SqlCommand
objCommand = New SqlCommand(strRequest, objConnection)
objCommand.Parameters = sqlParameters 'objCommand.Parameters is readonly property
objCommand.Connection.Open()
objCommand.ExecuteNonQuery()
End Using
End Sub
But VS tell me that objCommand.Parameters is a readonly property...
I see two solutions :
Passing an array containing the parameter name, value and type, and looping through the array
Creating the string request with all the parameters like that : "DELETE FROM tbl_devis WHERE id_devis = " + TBDevis.Text ... but when there are 30 parameters, this is a dirty solution I guess ?
Which one would be the cleaner, strongest solution please ?
Thanks for your help !
ParamArray is what you're looking for.
Update your ExecuteRequest like this:
Public Sub ExecuteRequest(ByVal strRequest As String, ParamArray Params() As SqlParameter)
Dim strConnexion As String = "myConnectionString"
Using Conn As New SqlConnection(strConnexion), Cmd As New SqlCommand(strRequest, Conn)
Cmd.Parameters.AddRange(Params)
Conn.Open()
Cmd.ExecuteNonQuery()
End Using
End Sub
and then you can call it like
ExecuteRequest("DELETE FROM tbl_devis WHERE id_devis = #id_devis", New SqlParameter("#id_devis", CInt(TBDevis.Text)))
I would also suggest to create function sqlPar(Name As String, Value As Object) with few more overloads to simplify the call to
ExecuteRequest("DELETE FROM tbl_devis WHERE id_devis = #id_devis", sqlPar("#id_devis", TBDevis.Text))
ParamArray allows you to add undefined amount of arguments like this
ExecuteRequest("SELECT ID FROM Table WHERE ID IN (#A, #B, #C, #D)", sqlPar("#A", 1), sqlPar("#B", 2), sqlPar("#C", 3), sqlPar("#D", 4))
You should ALWAYS use SqlParameter instead of string concatenation to prevent SQL injections.
You should ALWAYS use Using for IDisposable resources as well.
Related
I want to identify the properties of specific object that it receives when the method is called and put values in it from the db result that I got. I've searched about it but I'm currently stucked in how I should proceed from here. Here is my code..
Public Class DBModel
Public Sub getFromDB(ByRef lists As List(Of Object), ByVal classType As Type, ByVal tblName as String)
Dim strSql As String = "SELECT * FROM " & tblName
Dim props = classType.GetProperties()
Try
Using cnn As New SqlConnection("Data Source = .\; Initial Catalog = DBName;" & "Integrated Security = True;")
Using cmd As New SqlCommand(strSql, cnn)
cnn.Open()
Using dr As SqlDataReader = cmd.ExecuteReader()
While dr.Read
For Each prop In props
For i As Integer = 0 To dr.VisibleFieldCount - 1
prop = dr.GetValue(i)
Next
Next
lists.Add(props)
End While
End Using
End Using
End Using
Catch e As Exception
MessageBox.Show(e.ToString())
End Try
End Sub
End Class
I'm calling here the getFromDB method to populate the list of customers in this class, but I'll also call the getFromDB method from other classes with another different set of properties..
Public Class CustomerCtrler
private _CustomerList As New List(Of Customer)
Public Sub New()
Dim dbModel As New DBModel
Dim cust As New Customer
dbModel.getFromDB(_CustomerList, cust.GetType, "CustTbl")
End sub
End Class
Public Class Customer
Public Property custID As Integer
Public Property FirstName As String
Public Property LastName As String
Public Property DateRegistered As DateTime
End Class
But I got a InvalidCastException, so I've searched about converting the data types but I got: "Value type of Integer cannot be converted into PropertyInfo" at the 'prop = dr.GetValue(i)' line..
I'm quite new to object oriented programming so I'm sorry if there's a lot of mistakes there but your help will be really appreciated..
I would tend to go with something like this:
Public Function GetListFromDatabase(Of T As New)(tableName As String) As List(Of T)
Dim itemType = GetType(T)
Dim allProperties = itemType.GetProperties()
Dim items As New List(Of T)
Using connection As New SqlConnection("connection string here"),
command As New SqlCommand($"SELECT * FROM [{tableName}]", connection)
connection.Open()
Using reader = command.ExecuteReader()
Dim columnNames = reader.GetColumnSchema().
Select(Function(column) column.ColumnName).
ToArray()
'Ignore properties that don't have a corresponding column.
Dim properties = allProperties.Where(Function(prop) columnNames.Contains(prop.Name)).
ToArray()
Do While reader.Read()
'We can do this because we have specified that T must have a
'parameterless constructor by using "As New" in the method declaration.
Dim item As New T
For Each prop In properties
prop.SetValue(item, reader(prop.Name))
Next
items.Add(item)
Loop
End Using
End Using
Return items
End Function
You can then do this:
_CustomerList = dbModel.GetListFromDatabase(Of Customer)("CustTbl")
You can obviously create a variation on that if you really want to pass in an existing list but I don't see the point in that unless the list might already contain items.
EDIT: Here is an alternative method to get the data reader column names. I haven't tested it so it may be that "COLUMN_NAME" isn't quite right but it will be something very close to this:
Dim schemaTable = reader.GetSchemaTable()
Dim columnNames = schemaTable.Rows.
Cast(Of DataRow).
Select(Function(row) CStr(row("COLUMN_NAME"))).
ToArray()
I've been trying to create a function that returns an object of type. This function below is currently inside my dataclass and can be called from any object that inherits the dataclass. My problem is when I try and return my object. It populates just fine while I am inside the function. When I get back out to the calling object its all empty like it's a new object.
Here's the function....
Public Overloads Function GetClassFromDB(ByVal ID As Integer) As Object
Try
Dim BaseObject As New Object
Dim objDerived As Type = MyBase.GetType()
Dim TableName As String = String.Empty
Dim SQL As New LottoPayload.SQLiDataClass
Dim SQLString As String = String.Empty
Dim SQLCommand As SQLiteCommand = Nothing
Dim SQLConnection As SQLiteConnection = Nothing
Dim SQLiteDRObj As SQLiteDataReader = Nothing
Dim SQLResultsTable As New DataTable
'Create an instance of the base object
BaseObject = Activator.CreateInstance(objDerived)
'Get the tablename from the object
TableName = objDerived.GetProperty("TableName").GetValue(BaseObject, Nothing).ToString
SQLString = "SELECT * FROM " & TableName & " WHERE ID = '" & ID.ToString & "' LIMIT 1"
SQLConnection = SQL.GetSqlConnection()
SQLConnection.Open()
SQLCommand = New SQLiteCommand(SQLConnection)
SQLCommand.CommandText = SQLString
SQLiteDRObj = SQLCommand.ExecuteReader()
SQLResultsTable.Load(SQLiteDRObj)
If SQLResultsTable.Rows.Count > 0 Then
For Each Row As DataRow In SQLResultsTable.Rows
For Each Column As DataColumn In Row.Table.Columns
Dim ColumnName As String = Column.ColumnName.ToString
Console.WriteLine(Row.Item(ColumnName))
Dim ColumnValue As Object = Row.Item(ColumnName)
Console.WriteLine(Column.DataType.Name & " ")
Select Case Column.DataType.Name
Case "Int64"
Dim ConvertedValue As Integer
Integer.TryParse(ColumnValue.ToString, ConvertedValue)
objDerived.GetProperty(ColumnName).SetValue(BaseObject, ConvertedValue, Nothing)
Case "Double"
Dim ConvertedValue As Double
Double.TryParse(ColumnValue.ToString, ConvertedValue)
objDerived.GetProperty(ColumnName).SetValue(BaseObject, ConvertedValue, Nothing)
Case "String"
Dim ConvertedValue As String
ConvertedValue = ColumnValue.ToString
objDerived.GetProperty(ColumnName).SetValue(BaseObject, ConvertedValue, Nothing)
Case Else
Dim ConvertedValue As String
ConvertedValue = "NotSet"
objDerived.GetProperty(ColumnName).SetValue(BaseObject, ConvertedValue, Nothing)
End Select
Next
Next
End If
Return objDerived
SQLiteDRObj.Close()
SQLConnection.Close()
SQLConnection.Dispose()
'Return False
Catch ex As Exception
Return False
End Try
End Function
and this is how it would be called.
Dim objResults As New myapp.objResult
Dim ID As Integer = 3
objResults.GetClassFromDB(ID)
Question #1 - When I return the object from GetClassFromDB objResults is empty eventhough it had data up until the Return objDerived. Why is this the case?
Question #2 - Is there a way I can eliminate that Select Case area and consolidate the code by using reflection?
Thanks in advance for the help.
Shouldn't you be returning BaseObject rather than objDerived, given that objDerived is actually a Type object that represents the type of the object created rather than the object itself? Maybe it would be clearer if you didn't use appalling names like objDerived in the first place.
The problem is that your method returns an object, but you are not assigning it to a variable anywhere. You need to assign the return value of the method to a variable
Dim someVariable As Object = objResults.GetClassFromDB(ID)
Also, your method is strange in that it returns an object if the code runs successfully but a boolean if there is an exception, which you silently swallow (also a bad practice).
You are returning the wrong object. objDerived is a Type, not the object that you are setting. You need to return BaseObject. Also, realize that your code to close and dispose the SqlCommand and Connection won't be hit since it's after the return operator. It's best here to wrap the connection and command in Using clauses which will ensure they will be disposed once they go out of scope. See if the following works:
Public Overloads Function GetClassFromDB(ByVal ID As Integer) As Object
Try
Dim BaseObject As New Object
Dim objDerived As Type = MyBase.GetType()
Dim TableName As String = String.Empty
Dim SQL As New LottoPayload.SQLiDataClass
Dim SQLString As String = String.Empty
Using SQLConnection As SQLiteConnection = Nothing
Using SQLCommand As SQLiteCommand = Nothing
Dim SQLiteDRObj As SQLiteDataReader = Nothing
Dim SQLResultsTable As New DataTable
'Create an instance of the base object
BaseObject = Activator.CreateInstance(objDerived)
'Get the tablename from the object
TableName = objDerived.GetProperty("TableName").GetValue(BaseObject, Nothing).ToString
SQLString = "SELECT * FROM " & TableName & " WHERE ID = '" & ID.ToString & "' LIMIT 1"
SQLConnection = SQL.GetSqlConnection()
SQLConnection.Open()
SQLCommand = New SQLiteCommand(SQLConnection)
SQLCommand.CommandText = SQLString
SQLiteDRObj = SQLCommand.ExecuteReader()
SQLResultsTable.Load(SQLiteDRObj)
If SQLResultsTable.Rows.Count > 0 Then
For Each Row As DataRow In SQLResultsTable.Rows
For Each Column As DataColumn In Row.Table.Columns
Dim ColumnName As String = Column.ColumnName.ToString
Console.WriteLine(Row.Item(ColumnName))
Dim ColumnValue As Object = Row.Item(ColumnName)
Console.WriteLine(Column.DataType.Name & " ")
Select Case Column.DataType.Name
Case "Int64"
Dim ConvertedValue As Integer
Integer.TryParse(ColumnValue.ToString, ConvertedValue)
objDerived.GetProperty(ColumnName).SetValue(BaseObject, ConvertedValue, Nothing)
Case "Double"
Dim ConvertedValue As Double
Double.TryParse(ColumnValue.ToString, ConvertedValue)
objDerived.GetProperty(ColumnName).SetValue(BaseObject, ConvertedValue, Nothing)
Case "String"
Dim ConvertedValue As String
ConvertedValue = ColumnValue.ToString
objDerived.GetProperty(ColumnName).SetValue(BaseObject, ConvertedValue, Nothing)
Case Else
Dim ConvertedValue As String
ConvertedValue = "NotSet"
objDerived.GetProperty(ColumnName).SetValue(BaseObject, ConvertedValue, Nothing)
End Select
Next
Next
End If
End Using ' Sql Command
End Using ' Sql Connection
Return BaseObject
'Return False
Catch ex As Exception
Return False
End Try
End Function
End Class
From your second screen shot, you don't appear to be setting anything to the return of the GetClassFromDB(ID) method. It should probably be something like:
Dim result = objResults.GetClassFromDB(ID)
' Do something with result
I need to return some data from a web service that looks something like this:
data.page = 1
data.count = 12883
data.rows(0).id = 1
data.rows(0).name = "bob"
data.rows(1).id = 2
data.rows(1).name = "steve"
data.rows(2).id = 3
data.rows(2).name = "fred"
I have no idea how to do this. I've returend simple types and simple arrays, but never an object like this.
The data source is a sql Database. The target is a javascript/ajax function. I'm currently successfully returning the rows themselves as a dataset and it works, but I need to add the count and a couple other "parent level" variables.
For the sake of full disclosure, here is the code that is working:
<WebMethod()> _
Public Function rptPendingServerRequests() As DataSet
Dim connetionString As String
Dim connection As SqlConnection
Dim command As SqlCommand
Dim adapter As New SqlDataAdapter
Dim ds As New DataSet
Dim sql As String
connetionString = "..."
sql = "SELECT usm_request.request_id, usm_request.status, usm_request.req_by_user_id " +
"FROM usm_request " +
"WHERE usm_request.request_id in " +
"(SELECT distinct(usm_request.request_id) from usm_request, usm_subscription_detail WHERE usm_request.request_id = usm_subscription_detail.request_id " +
"AND usm_subscription_detail.offering_id = 10307) ORDER BY usm_request.request_id DESC"
connection = New SqlConnection(connetionString)
Try
connection.Open()
command = New SqlCommand(sql, connection)
adapter.SelectCommand = command
adapter.Fill(ds)
adapter.Dispose()
command.Dispose()
connection.Close()
Return ds
Catch ex As Exception
End Try
End Function
And I'm trying to consume it with FlexiGrid. I've been working at it for a few hours with no luck. I basically need to convert the PHP at the following site to .net
http://code.google.com/p/flexigrid/wiki/TutorialPropertiesAndDocumentation
I think that you would be much better off just creating a couple of classes and moving the data from the database into these classes. For example:
Public Class MyDataClass
Public Property Page As Integer
Public ReadOnly Property Count As Integer
Get
If Me.Rows IsNot Nothing Then
Return Me.Rows.Count
Else
Return 0
End If
End Get
End Property
Public Property Rows As List(Of MyDataRow)
' Parameterless constructor to support serialization.
Public Sub New()
Me.Rows = New List(Of MyDataRow)
End Sub
Public Sub New(wPage As Integer, ds As DataSet)
Me.New()
Me.Page = wPage
For Each oRow As DataRow In ds.Tables(0).Rows
Dim oMyRow As New MyDataRow
oMyRow.Id = oRow("id")
oMyRow.Name = oRow("Name")
Me.Rows.Add(oMyRow)
Next
End Sub
End Class
Public Class MyDataRow
Public Property Id As Integer
Public Property Name As String
' Parameterless constructor to support serialization
Public Sub New()
End Sub
End Class
Then change the return type of the method to MyDataClass and change the return to:
Return New MyDataClass(1, ds)
I have a function that returns a list of account numbers as an Arraylist. I am trying to use each account as a command parameter in another sub routine to get more data about each account number. This only returns the data for the last account number in the arraylist. I need to use each account number, call the database, get the additional information and store ALL of the data into a Gridview (databind). Example: If I had 3 account numbers in my arraylist return 3 rows of data to the gridview. I am struggling with how to get ALL of the information for each value (account number) in the Arraylist. Can someone point me in the right direction?? I think this can be done but I am not certain if my approach is correct or not. Perhaps I need to create datatables that contain the additional information for each value passed via the arraylist....Any Ideas??
#jwatts1980 thanks for the comment: I will try to clarify. I have an arraylist of account numbers (and maybe this is where I am off track) I am trying to use the values in this ArrayList as command parameters in another call to a different table/file that returns more info on those accounts. I will provide a portion of the code to help clarify what it is I am attempting to do:
Private Function ReturnMultAccts(ByVal strAcct) As ArrayList
Dim acctsDetail As New ArrayList
Dim dsn As String = ConfigurationManager.ConnectionStrings.ConnectionString
Dim sql As String = "SELECT DISTINCT * FROM FILE WHERE ACCTNUM=?"
Using conn As New OdbcConnection(dsn)
Using cmd As New OdbcCommand(sql, conn)
conn.Open()
cmd.Parameters.Add("ACCTNUM", OdbcType.VarChar, 20).Value = strAcct
Dim rdrUsers As OdbcDataReader = cmd.ExecuteReader()
If rdrUsers.HasRows Then
While rdrUsers.Read()
acctsDetail.Add(Trim(rdrUsers.Item("ACCTNUM")))
End While
End If
rdrUsers.Close()
conn.Close()
End Using
End Using
This returns an Arraylist of Account Numbers (Lets say it is 3 acct numbers). I call this Function from another Sub:
Private Sub GetMoreAcctInfo(ByVal strAcct)
'Create New ArrayList
Dim MultAccts As New ArrayList
'Pass strAcct to Function to get Multiples
MultAccts = ReturnMultAccts(strAcct)
'Create the variable BachNum for the loop
Dim BachNum As String = MultAccts.Item(0)
For Each BachNum In MultAccts
'Get All of the necessary info from OtherFile based on the BachNum for BOS's
Dim dsn As String = ConfigurationManager.ConnectionStrings.ConnectionString
Dim sql As String = "SELECT ACCTNUM, BILSALCOD1, BILSALCOD2, BILSALCOD3, OTHACCTNUM FROM OtherFile WHERE OTHACCTNUM=?" 'Equal to the items in the arraylist
Using conn As New OdbcConnection(dsn)
Using cmd As New OdbcCommand(sql, conn)
conn.Open()
cmd.Parameters.Add("OTHACCTNUM", OdbcType.VarChar, 20).Value = BachNum
Using adapter = New OdbcDataAdapter(cmd)
Dim DS As New DataSet()
adapter.Fill(DS)
GridView1.DataSource = DS
GridView1.DataBind()
End Using
End Using
End Using
Next
End Sub
Hopefully this clarifies what I am attempting to do...??
To elaborate on my suggestion, you will need a list of strongly typed objects. You can add those items to the list, then bind the list to the GridView.
I'll start at the beginning. You know what kind of data is coming from your database: ACCTNUM, BILSALCOD1, BILSALCOD2, BILSALCOD3, and OTHACCTNUM. So you can use those to create an object.
Friend Class AccountClass
Private pACCTNUM As string = ""
Private pBILSALCOD1 As string = ""
Private pBILSALCOD2 As string = ""
Private pBILSALCOD3 As string = ""
Private pOTHACCTNUM As string = ""
Public Property ACCTNUM() As string
Get
Return pACCTNUM
End Get
Set(ByVal value as string)
Me.pACCTNUM = value
End Set
End Property
Public Property BILSALCOD1() As string
Get
Return pBILSALCOD1
End Get
Set(ByVal value as string)
Me.pBILSALCOD1 = value
End Set
End Property
Public Property BILSALCOD2() As string
Get
Return pBILSALCOD2
End Get
Set(ByVal value as string)
Me.pBILSALCOD2 = value
End Set
End Property
Public Property BILSALCOD3() As string
Get
Return pBILSALCOD3
End Get
Set(ByVal value as string)
Me.pBILSALCOD3 = value
End Set
End Property
Public Property OTHACCTNUM() As string
Get
Return pOTHACCTNUM
End Get
Set(ByVal value as string)
Me.pOTHACCTNUM = value
End Set
End Property
Sub New(ByVal ACCTNUM As string, ByVal BILSALCOD1 As string, ByVal BILSALCOD2 As string, ByVal BILSALCOD3 As string, ByVal OTHACCTNUM As string)
Me.ACCTNUM = ACCTNUM
Me.BILSALCOD1 = BILSALCOD1
Me.BILSALCOD2 = BILSALCOD2
Me.BILSALCOD3 = BILSALCOD3
Me.OTHACCTNUM = OTHACCTNUM
End Sub
End Class
Then you rework the GetMoreAcctInfo() routine to use it.
Private Sub GetMoreAcctInfo(ByVal strAcct)
'Create New ArrayList
Dim MultAccts As ArrayList
'Pass strAcct to Function to get Multiples
MultAccts = ReturnMultAccts(strAcct)
'Create the variable BachNum for the loop
Dim BachNum As String
'Create the list to bind to the grid
Dim AcctInfo As New Generic.List(Of AccountClass)
'create the dataset
Dim DS As DataSet
For Each BachNum In MultAccts
'Get All of the necessary info from OtherFile based on the BachNum for BOS's
Dim dsn As String = ConfigurationManager.ConnectionStrings.ConnectionString
Dim sql As String = "SELECT ACCTNUM, BILSALCOD1, BILSALCOD2, BILSALCOD3, OTHACCTNUM FROM OtherFile WHERE OTHACCTNUM=?" 'Equal to the items in the arraylist
Using conn As New OdbcConnection(dsn)
Using cmd As New OdbcCommand(sql, conn)
conn.Open()
cmd.Parameters.Add("OTHACCTNUM", OdbcType.VarChar, 20).Value = BachNum
Using adapter = New OdbcDataAdapter(cmd)
DS = New DataSet()
adapter.Fill(DS)
For Each t As DataTable In DS.Tables
For Each r As DataRow In t.Rows
AcctInfo.Add(new AccountClass(r("ACCTNUM"), r("BILSALCOD1"), r("BILSALCOD2"), r("BILSALCOD3"), r("OTHACCTNUM")))
Next
Next
End Using
End Using
End Using
Next
GridView1.DataSource = AcctInfo
GridView1.DataBind()
End Sub
I'm trying to pass a list of objects to my WCF service, but it doesn't seem I can pass an object list from my console test application. I have an error that states:
Value of type
'System.Collections.Generic.List(Of
ConsoleTestingApp.ServiceReference1.LetterVariables)'
cannot be converted to '1-dimensional
array of
ConsoleTestingApp.ServiceReference1.LetterVariables'.
On this line:
Console.WriteLine(client.GetLetterObj("1", "1", "0", lstVariables))
Anybody have any ideas what I need to do?
Thanks,
Jason
'******* Code ***********
My test console app looks like this:
Dim Variables As LetterVariables
Dim lstVariables As New List(Of LetterVariables)
Variables = New LetterVariables
Variables._Sort = 10
Variables._key = "Letter Number"
Variables._value = "10"
lstVariables.Add(Variables)
Variables = New LetterVariables
Variables._Sort = 20
Variables._key = "Amount"
Variables._value = "$200"
lstVariables.Add(Variables)
Console.WriteLine(client.GetLetterObj("1", "1", "0", lstVariables))
Console.WriteLine("Finished")
Here's the contract:
<OperationContract()> _
Function GetLetterVariablesObj(ByVal LetterSpecID As Int32) As List(Of LetterVariables)
Here's the svc:
Public Function GetLetterObj(ByVal LetterID As String, ByVal StateID As String, ByVal CompID As String, ByVal lstVars As System.Collections.Generic.List(Of LetterVariables)) As String Implements ILetterWriter.GetLetterObj
Dim SQLcon As New SqlClient.SqlConnection
Dim SQLcmd As New SqlClient.SqlCommand
Dim Variables As LetterVariables
Dim tblVars As DataTable
'Load the datatable to be passed to SQL Server
tblVars = New DataTable
tblVars.TableName = "LetterVariables"
tblVars.Columns.Add("Order")
tblVars.Columns.Add("Key")
tblVars.Columns.Add("Value")
For Each Variables In lstVars
tblVars.Rows.Add(Variables.Sort, Variables.Key, Variables.Value)
Next
'Connect to the database
SQLcon.ConnectionString = "Data Source=MySRVR;Initial Catalog=Sears;User ID=me;Password=mypass;"
SQLcon.Open()
'Set the procedure name, type & connection
SQLcmd.CommandText = "sp_cmd"
SQLcmd.CommandType = CommandType.StoredProcedure
SQLcmd.Connection = SQLcon
'Pass the parameters
SQLcmd.Parameters.AddWithValue("#LetterID", LetterID)
SQLcmd.Parameters.AddWithValue("#StateID", StateID)
SQLcmd.Parameters.AddWithValue("#CompID", CompID)
SQLcmd.Parameters.AddWithValue("#Vars", tblVars)
'Initialize the function string, execute the stored procedure
GetLetterObj = ""
GetLetterObj = SQLcmd.ExecuteScalar
'Close it all down
SQLcon.Close()
SQLcon.Dispose()
SQLcmd.Dispose()
End Function
This is because Collections are exposed as arrays by default. You should be able to pass in lstVariables.ToArray(). See this post for the explanation as to why it is the way it is.