I'm having some trouble with a DataView in ASP.NET. My code is pretty simple, I just want to pull information from an MS Access table called COURSEINFO, put the information into a DataSet, and use a DataView as the source of a GridView. I realize that it would be easier to just use an SQL Data Source to populate the GridView, but I want to use the DataView so that I can expand it later.
However, this line of code gives me a compile error:
dv = dv = ds.Tables(0).DefaultView
where dv is my DataView and ds is my DataSet. The error occurs after the "=", dv = ds.Tables(0).DefaultView appears underlined in blue.
Oddly, this code worked for me a few days ago, and I don't know why is doesn't anymore. The error that I get says: "Value of type 'System.Data.DataView' cannot be converted to 'WebApplication1.DataView'
Here's my code:
Dim connetionString As String
Dim connection As OleDb.OleDbConnection = New OleDb.OleDbConnection("Provider=Microsoft.ACE.OleDb.12.0;" & "Data Source=|DataDirectory|\EXSpring2014.accdb")
Dim command As OleDb.OleDbCommand
Dim adapter As New OleDb.OleDbDataAdapter
Dim sql As String = "Select * from COURSEINFO"
Dim ds As DataSet = New DataSet
Dim dv As DataView
connection.Open()
command = New OleDb.OleDbCommand(sql, connection)
adapter.SelectCommand = command
adapter.Fill(ds, "Create_DataView")
adapter.Dispose()
command.Dispose()
connection.Close()
dv = ds.Tables(0).DefaultView
GridView1.DataSource = dv
When I actually try to load the web form, I get an error in the HTML code. I you would like to see it, the URL is
http://ime1.ime.calpoly.edu/ime312_move1x/ime312_5/WebApplication1/camtasia.aspx
Finally, while working on this, I generated a method stub that I didn't end up needing for "DataView". I'm thinking that this might be a cause, but I don't know what to do about it.
The red herring is the error:
"Value of type 'System.Data.DataView' cannot be converted to 'WebApplication1.DataView'
What does the compiler try to explain here?
As the .Net framework adheres strongly typing it requires that developers only assign values to variables that are of the same type. It is understandle that we can't assign a List<String> to a Form. If you try that you get a similar error you see.
The confusion starts when you name two types the same, in this case DataView. You can't have the same named typed within the same namespace so there must be two DataView types which indeed there are: One in the namespace System.Data and one in the namespace WebApplication1.
Not seen in your code but I have to assume you created a webpage or another type called DataView somewhere in your web project. If the namespace isn't explictely specified the compiler asssumes the type that is in the current namespace should be used.
To solve this compile error you simply have to be excplicit which DataView you want a variable to be.
Change the type of your dv to be of type System.Data.DataView and all should be fine:
Dim dv As System.Data.DataView
Have a read on the artcile Namespaces in Visual Basic for some more background.
Related
I have the following code:
Dim da As SqlDataAdapter
Dim rst As DataRow = MyrstEdit("SELECT * FROM tblHotels WHERE ID = 19",, da).Rows(0)
rst("HotelName") = "My Cool"
rst("FirstName") = "Albert"
rst("City") = "Edmonton"
da.Update(rst.Table)
So, the above is nice and short. And it works rather nice.
And of course this being asp.net, then centralizing things like connection code (that I don't have to create over and over) is a also rather nice. And why bother with a connection during coding, so above reduces coding workload.
How then can I ensure the connection object is disposed and closed correctly?
From reading, since I do not open the conneciton, then
The Fill() does open, then close.
And I have to assume that the da.Update() ALSO must then by logic open, then close the conneciton.
However, I still should clean up the connection object after above is done.
Question:
Is disposing of the sql data adaptor object sufficient here to also dispose the connection object that the data adaptor is using?
eg:
da.Dispose()
The MyRstEdit routine is this:
Public Function MyrstEdit(strSQL As String,
Optional strCon As String = "",
Optional ByRef oReader As SqlDataAdapter = Nothing) As DataTable
' can pass custom connection string - if not passed, then default
If strCon = "" Then
strCon = GetConstr() ' global func to get application con string
End If
Dim mycon As New SqlConnection(strCon)
oReader = New SqlDataAdapter(strSQL, mycon)
Dim rstData As New DataTable
Dim cmdBuilder = New SqlCommandBuilder(oReader)
Try
oReader.Fill(rstData)
oReader.AcceptChangesDuringUpdate = True
Catch
End Try
Return rstData
End Function
So, the return sqlDataAdaptor object must be holding the connection, since the scope in above routine is limited.
So do I have to dispose the connection object?
Can I do this with the sqlAdaptor?
I can't dispose/close the connection object in above sub, since my da.Update() in the calling code still needs that connection until I do the update.
How then can I ensure the connection object is disposed and closed correctly?
Don't worry about it; it's not your job. DataAdapter makes it, DataAdapter will clean it up
However, I still should clean up the connection object after above is done
No, for the aforementioned reason
Is disposing of the sql data adaptor object sufficient here to also dispose the connection object that the data adaptor is using?
Yes, unless you have good reason to believe that Microsoft's code has a critical flaw and their classes will benefit from your code micromanaging the resources they create..
You can also read the reference source if you want to know what they do internally
The MyRstEdit routine is this:
It's hard to understand why it exists in that form; you'd be better off just passing a datatable around and creating dataadapters as and when you need them. MyRstEdit isn't well named; it doesn't seem to edit anything, it always overwrites the passed in adapter with stuff that a any passed in adapter might already know (the connstr and sql) and then doesn't really do anything that couldn't just be put to
Using da As New SqlDataAdapter("SELECT * FROM tblHotels WHERE ID = 19", GetConStr())
Dim dt as New DataTable
da.Fill(dt)
Dim rst = dt(0)
rst("HotelName") = "My Cool"
rst("FirstName") = "Albert"
rst("City") = "Edmonton"
New SqlCommandBuilder(da)
da.Update(rst.Table)
End Using
About the most useful thing it does is apply a command builder, but that's only a single line and only needed for an update..
Perhaps you could create an extension method that applies to a DataAdapter, that gets the first row, so you could say:
Using da As SqlDataAdapter("SELECT * FROM tblHotels WHERE ID = 19", GetConStr())
Dim rst = da.FirstRow()
rst("HotelName") = "My Cool"
rst("FirstName") = "Albert"
rst("City") = "Edmonton"
New SqlCommandBuilder(da)
da.Update(rst.Table)
End Using
But it isn't saving much over "just using it how MS intended", and there is still that block of "data columns accessed by string and hence intellisense can't help" in the middle.
If you're looking to find ways of making your SqlDataAdapter life easier it might be an opportunity to skip it and have a look at Dapper instead; a set of extension methods on a SqlConnection that map classes to and from:
Using c as New SqlConnection(GetConStr())
Dim h = Await c.SingleAsync(Of Hotel)("SELECT * FROM tblHotels WHERE ID = #id", New With { .ID = 19 } )
h.HotelName = "My Cool"
h.FirstName = "Albert"
h.City = "Edmonton"
Await c.ExecuteAsync("UPDATE tblHotels SET HotelName = #HotelName, FirstName = #FirstName, City = #City WHERE ID = #ID", h)
End Using
You still have to write the queries, but it's a one time op, or you could look at something like Dapper Contrib.. The main use here is that instead of being some DataRow object you access by "string column names" you have a first class VB.NET object - Hotel, with named typed proeprties, and dapper can create them from SQL queries and put their values directly into SQL parameters (another thing that is absent from your existing code)
Why is there no Shared function for ExecuteDataTable in SQLHelper.vb. There is an: ExecuteReader, ExecuteDataset and ExecuteScaler.
This is not a problem as I will write my own. I am just wandering why this is the case. I would normally use a DataReader but I am writing a data logic layer and the DataTable needs to outlive the connection (DataReaders cannot outlive a connection).
ExecuteDataset() will already do what you need. A dataset is, in one sense, just a collection of DataTables.
I would normally use a DataReader but I am writing a data logic layer and the DataTable needs to outlive the connection (DataReaders cannot outlive a connection).
In this case, may I suggest that instead of building an ExecuteDatatable() method, you build an ExecuteEnumerable() method that uses a DataReader in an Iterator block. The code would look something like this:
Public Shared Iterator Function ExecuteEnumerable(Of T)( ... ) As IEnumerable(Of T)
Using cn As New SqlConnection( ... ), _
cmd As New SqlCommand( ... )
'As needed
'cmd.Parameters.Add( ... ).Value = ...
Using rdr As SqlDataReader = cmd.ExecuteReader()
While rdr.Read()
Yield transform(rdr)
End While
End Using
End Using
End Function
You'll notice that I skipped over a few things. I'm not familiar with the existing SqlHelper.vb file, and as you would want to match existing style I left room in the code for you to adapt for that. However, there are two important pieces I want to call out:
Note the cmd.Parameters.Add() call. One common failing of utility sql help classes is that they fail to adequately provide for query parameters. All to often the result is horribly insecure code. If you don't have a way right now to pass parameter data for your existing methods, you need to make one. That is priority 1.
The transform(rdr) call there will use a Func(IDataRecord, T) delegate that must be supplied as an argument to the function. For the ExecuteEnumerable() iterator concept to work, you must take a copy of the current values in the SqlDataReader object on each iteration. You could set up some kind of generic data transfer object here, as is done with the DataRow type using in a DataTable. However, rather than spending cpu and memory time creating a copy into a generic data transport object of some type, I prefer to use a delegate to have the code copy it directly into a strongly-typed business object. The downside is needing to send instructions on how to do that for your specific object with every call to method. Most often, though, this is easy enough to do with a shared factory method on your business object.
We can create same as DataSet like
' Execute a SqlCommand (that returns a resultset) against the specified SqlConnection
' using the provided parameters.
' e.g.:
' Dim dt As DataTable = ExecuteDataTable(conn, CommandType.StoredProcedure, "GetOrders", new SqlParameter("#prodid", 24))
' Parameters:
' -connection - a valid SqlConnection
' -commandType - the CommandType (stored procedure, text, etc.)
' -commandText - the stored procedure name or T-SQL command
' -commandParameters - an array of SqlParamters used to execute the command
' Returns: A dataset containing the resultset generated by the command
Public Overloads Shared Function ExecuteDataTable(ByVal connection As SqlConnection, _
ByVal commandType As CommandType, _
ByVal commandText As String, _
ByVal ParamArray commandParameters() As SqlParameter) As DataTable
If (connection Is Nothing) Then Throw New ArgumentNullException("connection")
' Create a command and prepare it for execution
Dim cmd As New SqlCommand
Dim dt As New DataTable
Dim dataAdatpter As SqlDataAdapter
Dim mustCloseConnection As Boolean = False
PrepareCommand(cmd, connection, CType(Nothing, SqlTransaction), commandType, commandText, commandParameters, mustCloseConnection)
Try
' Create the DataAdapter & DataSet
dataAdatpter = New SqlDataAdapter(cmd)
' Fill the DataSet using default values for DataTable names, etc
dataAdatpter.Fill(dt)
' Detach the SqlParameters from the command object, so they can be used again
cmd.Parameters.Clear()
Finally
If (Not dataAdatpter Is Nothing) Then dataAdatpter.Dispose()
End Try
If (mustCloseConnection) Then connection.Close()
' Return the dataset
Return dt
End Function ' ExecuteDataTable
I'm using a dynamic pass-through query in Access 2010 to retrieve one or more records from a back-end database. After much trial and error, I plagiarized enough of the right code to retrieve the appropriate records and assign them to unbound text-boxes on my datasheet form during an OnLoad event. The only problem remaining is in displaying multiple records. I've verified that I AM retrieving multiple records, but the contents of each record's fields overwrite the previous values stored to the form's textbox controls, so I always end up with just a single record displayed in my datasheet when I expect to see anywhere from one to 10.
I'm sure it's a simple solution. Can someone please point it out to me?
Private Sub Form_Load()
Dim sqlString As String
sqlString = "SELECT Transmitter_ID, Receiver_ID, UTC_Date, Local_Date from Detections"
If Not IsNull(Me.OpenArgs) Then
sqlString = sqlString & " where " & OpenArgs
End If
Dim cnn As New ADODB.Connection
Dim cmd As New ADODB.Command
Dim rst As ADODB.Recordset
'Define and open connection
cnn.ConnectionString = "DRIVER={SQLite3 ODBC Driver};Database=z:\EWAMP\EWAMP_be_dev.sqlite"
cnn.Open
'Define ADO command
cmd.ActiveConnection = cnn
cmd.CommandText = sqlString
'Populate and enumerate through recordset
Set rst = cmd.Execute
If rst.EOF Then
MsgBox "Nothing found...", vbInformation + vbOKOnly
Exit Sub
Else
Do While Not rst.EOF
'// I'm guessing the problem is with my control assignments, here.
Me.cntl_Receiver_ID.Value = rst("Receiver_ID")
Me.cntl_Transmitter_ID.Value = rst("Transmitter_ID")
Me.cntl_UTC_Date.Value = rst("UTC_Date")
Me.cntl_Local_Date.Value = rst("Local_Date")
Debug.Print {Show me the four control values}
rst.MoveNext
Loop
End If
End Sub
Cheers!
DUHdley
I don't believe a form in Datasheet view can be used as an unbound form. But you can use the ADO recordset as the forms recordset.
Set Me.Recordset = rst
Then just be careful not to close your variable named rst until the form closes.
Another alternative solution is to use an in-memory, fabricated, disconnected ADO recordset. Basically, you'd end up creating a new recordset, append fields to it to match your existing recordset, and then move all the data into your new recordset. But I really don't see the point in doing this if you already have a valid, filled ADO recordset.
If you really need/want to display multiple records in an unbound form, I think you would have to use ActiveX controls such as the GridView, ListView, TreeView, or MSFlexGrid. I've noticed that most skilled, professional Access developers stay away from ActiveX controls as much as possible. If and when they do use them, they usually limit it to only the TreeView and the ListView, I think because they are about the only ActiveX controls that add enough value to be worth putting up with whatever problems they might introduce.
I suggest you take a look at this article concerning the differences between DAO and ADO.
http://www.utteraccess.com/wiki/index.php/Choosing_between_DAO_and_ADO
A reader in another forum pointed me to a solution similar to that posed by HK1, namely Set Me.Recordset = rst. That fixed my original problem, but created another.
First I re-bound my four textbox controls on the unbound form, and then modified the code significantly, using the sample from http://msdn.microsoft.com/en-us/library/ff835419.aspx. The revised code looks like this:
Private Sub Form_Load()
Dim sqlString As String
sqlString = "SELECT Transmitter_ID, Receiver_ID, UTC_Date, Local_Date from Detections"
If Not IsNull(Me.OpenArgs) Then
sqlString = sqlString & " where " & OpenArgs
End If
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
'Define and open connection
Set cn = New ADODB.Connection
cn.ConnectionString = "DRIVER={SQLite3 ODBC Driver};Database=z:\EWAMP\EWAMP_be_dev.sqlite;"
cn.Open
'Create an instance of the ADO Recordset class,
'and set its properties
Set rs = New ADODB.Recordset
With rs
Set .ActiveConnection = cn
.Source = sqlString
'// .LockType = adLockOptimistic
.LockType = adLockReadOnly
.CursorType = adOpenKeyset
'// .CursorType = adOpenStatic
.Open
End With
'Set the form's Recordset property to the ADO recordset
Set Me.Recordset = rs
Set cn = Nothing
Set rs = Nothing
End Sub
The form now display four rows for four returned records, 20 rows for twenty records, and on, up to at least 256k rows (as specified by my parameter set). The only remaining teeny tiny problem is that for four or more records, if I press the "last row" navigation button (>|), the local cursor sets focus to one or more of the intermediate rows, and the control's properties sheet refreshes vigorously (multiple times per second). If I have more form rows than can be displayed on the screen, I can not navigate or cursor to the last row. It's as though the record set is constantly being updated.
As you can see, I've played with the RecordSet LockType and CursorType properties (including adOpenDynamic and adOpenForwardOnly, both of which caused a run-time error with the Set Me.Recordset statement). Toggling the LockType between adLockOptimistic and AdLockReadOnly, and the CursorType between adOpenKeyset and adOpenStatic makes no difference in the retrieval performance (which is fantastically fast now!) or the apparent refresh rate (which is even faster, unfortunately).
Perhaps it's worth mentioning that the "Detections" table the sqlString "selects" from contains ~4M records. I was frustrated in my previous attempts to use a form with a data source bound to a passthrough query of this table, because the query always returned the entire 4M records to the client regardless of the filter/WhereClause/OpenArgs parameter I passed to the form. The solution shown above would be perfect if only I could close the connection (I've tried) or otherwise quiesce the RecordSet after I've invoked it once.
I'm trying to bind a RadComboBox to an ObjectDataSource whose select method returns a DataSet object. I get an error: "'FieldName', is neither a DataColumn nor a DataRelation for table Table". Just to be sure I'm not crazy, I tried the same with a DropDownList which worked perfectly. I found in Telerik's docs that they support using an ObjectDataSource that returns IEnumerable objects. So, am I correct that the RadControls don't support using ObjectDataSource when it is using a DataSet? Really?
The link you provided points to a different control. See here for the combobox, which is the control in your question title.
The combobox control easily accepts datatable objects from datasets as a source of what to display in the combobox.
Then the combobox control:
Select the GetData method (the only option) and then configure your combobox:
Run:
EDIT:
There seems to be no reason to use a ObjectDataSource if you are already using a dataset and SqlDataAdapter :
DataSet myDataset = new DataSet();
SqlConnection con = new SqlConnection(#"Data Source=J-PC\SQLEXPRESS;Initial Catalog=SO;Integrated Security=True");
SqlDataAdapter adapter = new SqlDataAdapter(#"SELECT TOP (25) Leg_FirstName FROM GRS_Legislator ORDER BY Leg_FirstName", con);
adapter.Fill(myDataset);
RadComboBox1.DataTextField = "Leg_FirstName";
RadComboBox1.DataValueField = "Leg_FirstName";
RadComboBox1.DataSource = myDataset;
RadComboBox1.DataBind();
I have a page with 3 dropdownlist, 2nd and 3rd dropdownlist are added with CascadingDropDown. 3rd dropdownlist will take parameters from 1st and 2nd dropdownlist. So, in current example for CascadingDropDown i have found from google, they are only passing one parameter to the WebService method. How can pass two parameters to the service method, so that my 3rd dropdownlist will based on the SelectedValue of 1st and 2nd dropdownlist?
<WebMethod()> _
Public Function GetTeams(ByVal knownCategoryValues As String, ByVal category As String) As CascadingDropDownNameValue()
Dim strConnection As String = ConfigurationManager.ConnectionStrings("nerdlinessConnection").ConnectionString
Dim sqlConn As SqlConnection = New SqlConnection(strConnection)
Dim strTeamQuery As String = "SELECT * FROM TEAM WHERE conf_id = #confid"
Dim cmdFetchTeam As SqlCommand = New SqlCommand(strTeamQuery, sqlConn)
Dim dtrTeam As SqlDataReader
Dim kvTeam As StringDictionary = CascadingDropDown.ParseKnownCategoryValuesString(knownCategoryValues)
Dim intConfId As Integer
If Not kvTeam.ContainsKey("Conference") Or Not Int32.TryParse(kvTeam("Conference"), intConfId) Then
Return Nothing
End If
cmdFetchTeam.Parameters.AddWithValue("#confid", intConfId)
Dim myTeams As New List(Of CascadingDropDownNameValue)
sqlConn.Open()
dtrTeam = cmdFetchTeam.ExecuteReader
While dtrTeam.Read()
Dim strTeamName As String = dtrTeam("team_name").ToString
Dim strTeamId As String = dtrTeam("team_id").ToString
myTeams.Add(New CascadingDropDownNameValue(strTeamName, strTeamId))
End While
Return myTeams.ToArray
End Function
This is the sample code i found! As you can see in the code, '#confid' will be passed from 2nd dropdownlist! So, hw do i modify this code to get the selected value from 1st dropdownlist as well??
Which web service are you referring to? Is it something you have written or someone else's webservice?
In case it is your webservice, update the method definition in it and pass two parameters. In case it is somone else's, contact the concerned person to know what best can be done.
It appears the poster is not asking about Web Services really, but about SqlCommand and adding parameters.
First, you should never EVER run sql straight from your web application like that. Put it in a stored procedure.
Second, you should run checks on the values coming in, because this is a good way for your web site users to use SQL injection attacks.
Now... Here is what you were looking for:
Dim strTeamQuery As String = "SELECT * FROM TEAM WHERE conf_id = #confid"
becomes
Dim strTeamQuery As String = "SELECT * FROM TEAM WHERE conf_id = #confid AND second_id = #secondId"
Then just add another one of these:
cmdFetchTeam.Parameters.AddWithValue("#confid", intConfId)
(with the other value, of course, like this)
cmdFetchTeam.Parameters.AddWithValue("#confid", intConfId)
cmdFetchTeam.Parameters.AddWithValue("#secondId", intSecondId)