I have had an issue I have after much stress, narrowed down to assigning a string to a datatable (cell?)
Dim dc As New DataColumn("Col1")
dc.DataType = System.Type.GetType("System.String")
ParameterTable.Columns.Add(dc)
For i = 0 To whereInValues.Count - 1
r = ParameterTable.NewRow
r.Item("Col1") = whereInValues(i).ToString
ParameterTable.Rows.Add(r)
Next
This is a small snippet of the code in question. It's a loop iterating through the whereInValues List(Of String) containing strings for the IN() values of an SQL Statement.
ParameterTable is my DataTable
This is assigned to the .value property of the parameter, which is set to Structured.
Basically, when I set to the value of the datatable using the .ToString method of the List, it shows fine in the debugger, but doesn't return any results from the database. When I set the value directly like:
r.Item(column) = "Barratt Homes"
It returns the correct results. I have tried wrapped a CStr() around the expression, but to no effect.
What is the difference between assigning the datatable value this way? Is .ToString to same as a System.String?
Sorry, I have found the answer to my question.
My code was automatically wrapping the strings of the List(Of String) with a ' character. When using parameters you obviously doesn't need that as it forms part of the string you are searching for. So when I was assign the string manually, I didn't have these characters there.
Stupid mistake...
Related
I'm made a new list of string and when I try to add something there it gives me an error: Argument 'Prompt' cannot be converted to type 'string
My code:
Dim variables As New List(Of String)
Try
variables.Append(CStr(TextBox1.Text))
variables.Append(CStr(TextBox2.Text))
MsgBox(variables)
Catch ex As Exception
MsgBox(ex.Message)
End Try
How can I fix that?
The error is coming from your use of MsgBox(). You're passing it the variables variable, and it doesn't know how to convert a List(Of String) to a String.
As stated by jmcilhinney, you should be using Add() instead of Append().
Additionally, you should use MessageBox.Show() instead of MsgBox().
As for the error, we can only assume you want to see all the current values in your List? If so, one solution is to use String.Join() and display that instead:
Dim variables As New List(Of String)
variables.Add(TextBox1.Text)
variables.Add(TextBox2.Text)
MessageBox.Show(String.Join(",", variables))
But your variables list should be declared at Form level so that you aren't creating a new one each time. It isn't clear from your post if this is the case or not.
You should be calling the Add instance method of your List(Of String). Append is an extension method and is not appropriate in that scenario. Append would be used for an enumerable list that you wanted to enumerate on the spot, e.g.
Dim names = {"Peter", "Paul", "Mary"}
Dim pNames = names.Where(Function(name) name.StartsWith("P"))
For Each pName In pNames.Append("Philip")
Console.WriteLine(pName)
Next
In that case the Append only affects the list being enumerated by the loop where it's used. The original list is unaffected.
You should be using the Add method:
Dim variables As List(Of String) = New List(Of String)
variables.Add(TextBox1.Text)
(No need for the redundant CStr as TextBox1.Text is already of type String.)
As #Mary will no doubt suggest - rightly! - always have Option Explicit and Option Strict set to On (and in my opinion Option Infer Off); it'll help you with any syntax issues.
And always pay attention to the syntax/compiler suggestions in the left margin which will give you clues/tips to fix or improve your code.
Lastly, refer to the Microsoft documentation if in doubt. It should be your first port-of-call if you're unsure of anything.
As several people have mentioned, use the .Add method not .Append.
The .Text property of a TextBox is already a String. No need to convert with CStr().
A message box displays a String. variables is not a String; it is a List(Of String). To see what is in your list use a For Each loop.
Private Sub OpCode()
Dim variables As New List(Of String)
variables.Add(TextBox1.Text)
variables.Add(TextBox2.Text)
For Each s In variables
MsgBox(s)
Next
End Sub
#SteveCinq is correct :-); turn on Option Strict.
I have Option Strict and Option Infer both set "On".
This code works fine:
Dim tBoxes = From t In MainForm.Frame2.Controls.OfType(Of TextBox).ToList
tBoxes.ToList().ForEach(Sub(c) c.DataBindings.Clear())
Why can't I combine them into the one line below (I believe it's related to the fact that the first line above does not set tBoxes to a list but remains an IEnumerable even though I am calling ToList, why is this?)
Dim tBoxes = From t In MainForm.Frame2.Controls.OfType(Of TextBox).ToList.ForEach(Sub(c) c.DataBindings.Clear())
This code results in an error
Expression does not produce a value
This might seem like much ado about nothing but it's not just the reduction to one line, I'd like to understand what's going on here.
VB.NET 2010
The problem is not the ToList call, but List.ForEach Method which is Sub, hence does not have a result and cannot be assigned to a variable.
If you want to use a single line, remove Dim tBoxes =.
Update In fact there is another problem in the above code.
Dim tBoxes = From t In MainForm.Frame2.Controls.OfType(Of TextBox).ToList
is equivalent to
Dim tBoxList = MainForm.Frame2.Controls.OfType(Of TextBox).ToList
Dim tBoxes = From t in tBoxList
so obviously tBoxes is IEnumerable<TextBox>.
Since the from t In .. part is unnecessary in this case, the "oneliner" should be something like this
MainForm.Frame2.Controls.OfType(Of TextBox).ToList.ForEach(Sub(c) c.DataBindings.Clear())
If you really need a query part, to avoid such confusions, don't forget to enclose it in (..) before calling ToList or other methods like Count, Any etc., like this
(from t In MainForm.Frame2.Controls.OfType(Of TextBox)).ToList.ForEach(Sub(c) c.DataBindings.Clear())
Small description but enough to understand
From t In MainForm.Frame2.Controls.OfType(Of TextBox) 'Filter all object of type text box
.ToList 'Convert IEnemerable(Of TextBox) to a IList type.
.ForEach(Sub(c) c.DataBindings.Clear())' Iterate through list and remove bindg of each text box
Issue is that .ForEach does not return any value so that there is nothing to assign the tBoxes object that you have created. It is just like a void method or Sub in VB.net.
In declaration of variables and objects, when exactly should I use "New" word, and when shouldn't I use it?
I know that I should declare a string without "New" word:
Dim mystring As String
I also know I should use it declaring a datatable:
Dim mytable As New Datatable()
New creates an object that is an instance of the specified class. If you just write the following then you have a reference, but the reference is Nothing as you didn't actually create a Datatable for it to refer to:
Dim mytable As Datatable
You don't typically use New for value types (Numbers, Dates, Booleans, Structures, Enums - a full list is here), as they always have a value (cannot be Nothing). For example this outputs 0:
Dim num as Int32
Console.WriteLine(num)
I wouldn't worry too much about this, but some value types (structures) can be initialised with New, which is somewhat inconsistent, for example:
Dim dec = New Decimal(2, 3, 4, True, 5)
Im trying to convert the output of a LINQ query to a datatable, I have the following code but it shows a syntax error in the (Of DataRow) part:
Dim X As New Entities
Dim query As IEnumerable(Of DataRow) = From cajero In X.CAJERO.AsEnumerable
Select cajero
Dim bla = query.CopyToDataTable(Of DataRow)()
I'm using this question as a guide:
Filling a DataSet or DataTable from a LINQ query result set
If i use
query.CopyToDataTable()
'instead of the overload
query.CopyToDataTable(Of DataRow)
it throws an invalidCastException.
I'm looking for an easy way to accomplish this task, and this seemed to be the easiest one, without too much code and having to implement any "shredder" methods or such, but if that's the only way, then please point me in the right direction.
This is the error that throws(I localized it to english, its a bit different in spanish):
Cannot convert object of type WhereSelectEnumerableIterator to object of type IEnumerable System.Data.DataRow
I have tried the following:
Declare an extension method that would create the datatable like this:
_
Public Function myToDataTable(Of T)(source As IEnumerable(Of T)) As DataTable
Dim properties As PropertyInfo() = GetType(T).GetProperties()
Dim output As New DataTable()
For Each prop In properties
output.Columns.Add(prop.Name, prop.PropertyType)
Next
For Each item In source
Dim row As DataRow = output.NewRow()
For Each prop In properties
row(prop.Name) = prop.GetValue(item, Nothing)
Next
output.Rows.Add(row)
Next
Return output
End Function
But it always throws an exception while adding the columns to the datatable:
DataSet does not support System.Nullable
I also changed the linq query to this:
Dim query = From cajero In X.CAJERO
Select cajero
Dim bla = query.myToDataTable
Following Jon's suggestion I found this question:
.NET - Convert Generic Collection to DataTable
Which just gave me the last bit of code I needed:
output.Columns.Add(prop.Name, If(Nullable.GetUnderlyingType(prop.PropertyType), prop.PropertyType))
and
row(prop.Name) = If(prop.GetValue(item, Nothing), DBNull.Value)
I believe the problem is that you're trying to convert an IEnumerable<T> of an arbitrary entity type - whereas CopyToDataTable() always requires the input to be a sequence of some kind of DataRow.
Unless your entity type actually derives from DataRow, that's not going to work. You could potentially write a LINQ query which creates a DataRow from each instance, but I believe you'll have to write that code yourself.
I believe in the question you referenced, the OP already had a strongly typed DataSet - at least the answer suggested that's the case.
Update
I decided to iterate through the Data.DataTable and trimmed the values there.
Utilizing SirDemon's post, I have updated the code a little bit:
Sub test(ByVal path As String)
Dim oData As GSDataObject = GetDataObj(path)
EmptyComboBoxes()
Dim oDT As New Data.DataTable
Try
Dim t = From r In oData.GetTable(String.Format("SELECT * FROM {0}gsobj\paths ORDER BY keyid", AddBS(path))) Select r
If t.Count > 0 Then
oDT = t.CopyToDataTable
For Each dr As Data.DataRow In oDT.Rows
dr.Item("key_code") = dr.Item("key_code").ToString.Trim
dr.Item("descript") = dr.Item("descript").ToString.Trim
Next
dataPathComboBox.DataSource = oDT
dataPathComboBox.DisplayMember = "descript"
dataPathComboBox.ValueMember = "key_code"
dataPathComboBox.SelectedIndex = 0
dataPathComboBox.Enabled = True
End If
Catch ex As Exception
End Try
End Sub
This works almost as I need it to, the data is originally from a foxpro table, so the strings it returns are <value> plus (<Field>.maxlength-<value>.length) of trailing whitespace characters. For example, a field with a 12 character length has a value of bob. When I query the database, I get "bob_________", where _ is a space.
I have tried a couple of different things to get rid of the whitespace such as:
dataPathComboBox.DisplayMember.Trim()
dataPathComboBox.DisplayMember = "descript".Trim.
But nothing has worked yet. Other than iterating through the Data.DataTable or creating a custom CopyToDataTable method, is there any way I can trim the values? Perhaps it can be done in-line with the LINQ query?
Here is the code I have so far, I have no problem querying the database and getting the information, but I cannot figure out how to display the proper text in the ComboBox list. I always get System.Data.DataRow :
Try
Dim t = From r In oData.GetTable("SELECT * FROM ../gsobj/paths ORDER BY keyid") _
Select r
dataPathComboBox.DataSource = t.ToList
dataPathComboBox.SelectedIndex = 0
'dataPathComboBox.DisplayMember = t.ToList.First.Item("descript")
dataPathComboBox.Enabled = True
Catch ex As Exception
Stop
End Try
I know that on the DisplayMember line the .First.Item() part is wrong, I just wanted to show what row I am trying to designate as the DisplayMember.
I'm pretty sure your code tries to set an entire DataRow to a property that is simply the name of the Field (in a strongly type class) or a Column (in a DataTable).
dataPathComboBox.DisplayMember = "descript"
Should work if the DataTable contains a retrieved column of that name.
Also, I'd suggest setting your SelectedIndex only AFTER you've done the DataBinding and you know you actually have items, otherwise SelectedIndex = 0 may throw an exception.
EDIT: Trimming the name of the bound column will trim just that, not the actual bound value string. You either have to go through all the items after they've been bound and do something like:
dataPathComboBox.Item[i].Text = dataPathComboBox.Item[i].Text.Trim()
For each one of the items. Not sure what ComboBox control you're using, so the item collection name might be something else.
Another solution is doing that for each item when it is bound if the ComboBox control exposes an onItemDataBound event of some kind.
There are plenty of other ways to do this, depending on what the control itself offers and what you choose to do.
DisplayMember is intended to indicate the name of the property holding the value to be displayed.
In your case, I'm not sure what the syntax will by since you seem to be using a DataSet, but that should be
... DisplayMember="Item['descript']" ...
in Xaml, unless you need to switch that at runtime in which case you can do it in code with
dataPathComboBox.DisplayMember = "Item['descript']"
Again, not 100% sure on the syntax. If you are using a strongly typed DataSet it's even easier since you should have a "descript" property on your row, but given hat your error indicates "System.DataRow" and not a custom type, I guess you are not.
Because I can't figure out the underlying type of the datasource you are using I suggest you to change commented string to
dataPathComboBox.DisplayMember = t.ElementType.GetProperties.GetValue(0).Name
and try to determine correct index (initially it is zero) in practice.