Querying datatable.AsEnumerable with LINQ - vb.net

I have a data table which I query to determine if a certain row exists, there are a few possible scenarios:
Rule 1:
Dim rt1 As EnumerableRowCollection(Of Double) = From row In dtCh.AsEnumerable() _
Order By row.Field(Of Int64)("ID") Descending
Where (row.Field(Of String)("L_TYPE") = "A" _
And row.Field(Of Int16)("Customer_Type") = 1)
Select row.Field(Of Double)("Price")
If rt1.Any() Then
return CType(rt1.FirstOrDefault(), Decimal)
End If
Rule 2:
Dim rt2 As EnumerableRowCollection(Of Double) = From row In dtCh.AsEnumerable() _
Order By row.Field(Of Int64)("ID") Descending
Where (row.Field(Of String)("L_TYPE") = "B" _
And row.Field(Of Int16)("Customer_Type") = 0)
Select row.Field(Of Double)("Price")
If rt2.Any() Then
return CType(rt2.FirstOrDefault(), Decimal)
End If
and there are 2 more rules, if I have a row returned for rule one, I use the price returned from the first query, if nothing has been returned from the first query then I move on to the second rule and use the price from the second one and move on to the third and fourth one if necessary...
But this seems a bit of a long winded way, I know all the possible scenarios and in which order I wanted to check the scenarios, is there any way of combining these and find out the price with one query?
Thanks

It's not 100% clear from your question but it seems you are assuming that there will only be one row corresponding to any given parameters e.g A1, B0 etc.
In your query you are using any() to determine if the list contains any elements and then trying to return Single() which will only work if there is only one element, so why are you using an Enumerable?
It would be better to look for the first item that corresponds to your condition and put your conditions in the order you want e.g
dtCh.AsEnumerable().OrderBy(Function(Row) Row.Field(Of Int64)("ID")).First(Function(Row) _
(Row.Field(Of String)("L_TYPE") = "A" And Row.Field(Of Int16)("Customer_Type") = 1) Or _
(Row.Field(Of String)("L_TYPE") = "B" And Row.Field(Of Int16)("Customer_Type") = 0)).Price
EDIT: Ok I didn't quite get what you are looking for. I don't know if it is possible to query multiple times in one statement but I have one solution I just tried which works. It may not be to everyones taste but I quite like it. (Wish I knew how to indent and line space in code blocks?!)
Dim Query = dtCh.AsEnumerable().OrderBy(Function(x) x.Id)
Dim Conditions =
{
Function(Row) Row.Field(Of String)("L_TYPE") = "A" And _
Row.Field(Of Int16)("Customer_Type") = 1,
Function(Row) Row.Field(Of String)("L_TYPE") = "B" And _
Row.Field(Of Int16)("Customer_Type") = 0
}.ToList()
For Each Condition In Conditions
Dim Price = Query.FirstOrDefault(Condition)
If Price IsNot Nothing
Price.Price 'Get your price here.
Exit For
End If
Next

Related

How to calculate the Amount in a selected rows on Datagridview - VB.NET

how to calculate the amount of Selected rows in my table
I only want to calculate the to Total amount with the Status of Printed = 'Y' and Receive = 'N'
https://i.stack.imgur.com/iDqPE.png
Heres my code, this code will only calculate the total of Column amount
Dim rows As Integer = 0
Dim total_amount As Double
Try
Do Until rows = DataGridView1.RowCount
Dim AMOUNT = DataGridView1.Rows(rows).Cells(1).Value
total_amount = total_amount + AMOUNT
rows = rows + 1
Loop
Catch ex As Exception
End Try
lblAmount.Text = total_amount
Dim dblValue As Double = total_amount
lblTotalWithheld.Text = (dblValue.ToString("N",CultureInfo.InvariantCulture))
If you want to do something for every row in the grid then loop through the Rows collection, e.g.
For Each row As DataGridViewRow In DataGridView1.Rows
'Use row here.
Next
If you want to do something for each selected row then loop through the SelectedRows collection, e.g.
For Each row As DataGridViewRow In DataGridView1.SelectedRows
'Use row here.
Next
You say:
how to calculate the amount of Selected rows in my table
but it's not clear whether you mean selected in the UI or with the specified values in the specified columns. Either way, the code inside the loop will be the same:
If CStr(row.Cells(0).Value) = "Y" AndAlso CStr(row.Cells(1).Value) = "N" Then
Dim amount = CDbl(row.Cells(2).Value)
'Use amount here.
End If
I've used arbitrary column indexes there, so you should change them as required.
EDIT:
If your actual criteria for processing involves the state of the data rather than the state of the UI then you should actually not be using the grid at all, but rather the data source. If you aren't already, you should start by populating a DataTable, binding that to a BindingSource and then binding that to the grid, e.g.
myDataAdapter.Fill(myDataTable)
myBindingSource.DataSource = myDataTable
myDataGridView.DataSource = myBindingSource
You can then loop through the BindingSource to get the desired data, e.g.
Dim amount As Double = 0
For Each rowView As DataRowView In myBindingSource
If CStr(rowView("Printed")) = "Y" AndAlso
CStr(rowView("Receive")) = "N" Then
amount += CDbl(rowView("Amount"))
End If
Next
You could even throw some LINQ at it:
Dim amount = myBindingSource.Cast(Of DataRowView)().
Where(Function(drv) CStr(drv("Printed")) = "Y" AndAlso
CStr(drv("Receive")) = "N").
Sum(Function(drv) CDbl(rowView("Amount")))
or, in query syntax:
Dim amount = (From drv As DataRowView In myBindingSource
Where CStr(drv("Printed")) = "Y" AndAlso
CStr(drv("Receive")) = "N")
Select CDbl(rowView("Amount"))).Sum()
Possibly the easiest option of all, though, is to use the Compute method of the DataTable, e.g.
Dim amount = CDbl(myDataTable.Compute("SUM(Amount)",
"Printed = 'Y' AND Receive = 'N'")

linq using order by, where, and select vb.net

I have a Linq query that I am passing to a list, and then to the view through the viewbag. I am trying to keep that list in a specific order, so that when I iterate through it I have control over the order in which it's displayed.
Here is the query:
ViewBag.attributes = (From row In db.tblCategory_Attributes
Where row.Item_Type_Identifier = itemType
Order By row.Category_Attribute_Identifier
Select CStr(row.Attribute_Name)
Distinct).ToList()
I am successfully passing this list to the view and iterating through it, but no matter what the values are always displayed in alphabetical order. Category_Attribute_Identifier is an integer that aligns with the order I would like these values to be displayed in.
I've played around with the order of my statements quite a bit and I'm not having any luck.
Can you tell me how to distinctly select the Attribute_Name's that correlate with my specific Item_Type_Identifier and order my results by the Category_Attribute_Identifier?
The Distinct is creating its own ordering again (because it shuffles through the result to filter out duplicates). Just do the sorting after the Distinct:
(From row In db.tblCategory_Attributes
Where row.Item_Type_Identifier = itemType
Select row
Distinct)
.OrderBy(Function(row) row.Category_Attribute_Identifier)
.Select(Function(row) CStr(row.Attribute_Name))
Try using Group By instead of Distinct
ViewBag.attributes = (From row In db.tblCategory_Attributes _
Where row.Item_Type_Identifier = itemType _
Order By row.Category_Attribute_Identifier) _
.AsEnumerable() _
.GroupBy(Function(r) r.Attribute_Name) _
.Select(Function(g) g.Key) _
.ToList()
Or use the extension method syntax which gives you the freedom of applying the extension methods in any order:
ViewBag.attributes = db.tblCategory_Attributes _
.Where(Function(row) row.Item_Type_Identifier = itemType) _
.Select(Function(row) New With {row.Attribute_Name, row.Category_Attribute_Identifier}) _
.Distinct() _
.OrderBy(Function(a) a.Category_Attribute_Identifier) _
.Select(Function(a) a.Attribute_Name) _
.ToList()
This simple test demonstrates that GroupBy preserves the order:
Public Shared Sub TestGroupOrder()
Dim a = New Integer() {6, 2, 4, 2, 7, 5, 3, 4}
Dim query = a.GroupBy(Function(i) i).[Select](Function(g) g.Key)
For Each i As Integer In query
Console.Write("{0} ", i)
Next
End Sub
Result in the console:
6 2 4 7 5 3

datatable.AsEnumerable doesn't work (basic example)

Dim x = From row In f_table.AsEnumerable()
Select row("Crop")
From what I understand, the "f_table.AsEnumerable" should make my search object ("row" in this case) a datarow object. This simple example runs without any exceptions but does not find any entries (This search works if I switch to an array of datarows that have been taken from f_table, in place of f_table.AsEnumerable).
Any ideas why AsEnumerable is not allowing for searching the rows of the table?
edited/added: The following is what I have, where "emptyrows" is a subset array of rows from f_table.
Dim emptyrows_grouped = From row In emptyrows
Order By row("Date"), row("Time")
Group By New With {.date = row("Date")}.date,
New With {.crop = row("Crop")}.crop
Into Group
What i want is this form:
Dim emptyrows_grouped = From row In f_table.AsEnumerable
Where row.Field(Of String)("SamplePosition") Like "Emp%"
Order By row("Date"), row("Time")
Group By New With {.date = row("Date")}.date,
New With {.crop = row("Crop")}.crop
Into Group
It works like this:
Dim query = dt.AsEnumerable
.Where(Function(dr) dr("column name").ToString = "something").ToList
This yields a List of DataRows where this column has the value of "something"
GroupBy:
Dim query = dt.AsEnumerable
.Where(Function(dr) dr("column name").ToString = "something")
.GroupBy(Function(dr) dr("column name"))
Never mind - I'm a gigantic fool today because f_table is the wrong datatable. I used the right one and it worked.
Dim emptyrows_grouped = From row In file_table.AsEnumerable
Where row.Field(Of String)("SamplePosition") ="Empty"
Order By row("Date"), row("Time")
Group By New With {.date = row("Date")}.date,
New With {.crop = row("Crop")}.crop
Into Group
Please excuse my wasting of your time!!

Enumeration yielded no results

I am trying to insert/update my table using the values in the grid
Given below is the code I'm using to get the productId in the grid:
Dim prodId = From row As DataRow _
In grdSale.Rows _
Where row.Item(0).ToString <> "" _
Select row.Item(0)
I am getting productid correctly. Given below is the code to get the value in QTY column with respect to the productId:
For Each id As Long In prodId
Dim intpdt As Long
intpdt = id
intQty = (From row As DataRow In grdSale.Rows Where _
row.Item(0).Equals(intpdt) _
Select row.Item("QTY")).FirstOrDefault()
Next
In intQty I am getting 0 but it should be 10 or 12 as you can see in the QTY column in the grid (Enumeration yielded no results).
Where am wrong?
Try doing this and see if you get the result you expected:
intQty = _
( _
From row As DataRow In grdSale.Rows Where _
CLng(row.Item(0)) = intpdt _
Select CInt(row.Item("QTY")) _
).FirstOrDefault()
Not sure what causes your issue, but you should use the Field extension method since it is strongly typed and supports nullable types. I also don't understand why you need the additional loop and query to find the quantity of each product. This should do both:
Dim prodQTYs = From row In grdSale.Rows.Cast(Of DataRow)()
Let ProductID = row.Field(Of String)("ProductId")
Let Quantity = row.Field(Of Long)("QTY")
Where Not String.IsNullOrWhiteSpace(ProductID)
Select New With {.ProductID = ProductID, .Quantity = Quantity}
Change the types to the appropriate ones.
Output:
For Each prodInfo In prodQTYs
Console.WriteLine("Product:{0} Quantity:{1}", prodInfo.ProductID, prodInfo.Quantity)
Next

New to Linq; need to grab multiple values from single row

I'm trying to retrieve multiple columns from a datatable, but only from a single row -- and then set properties based on those results. I've figured out how to run multiple queries to obtain single columns at a time, but there must be a way to combine it all into one query.
Here's what I thought might work:
Dim colSettingsQry = From r In Me.GridProcColumnSettings.AsEnumerable _
Where r("DataFieldNm") = colNm _
Select New With _
{ _
.uniqueNm = r.Field(Of String)("UniqueNm").Single(), _
.sortExpression = r.Field(Of String)("SortExpression").Single(), _
.headerTxt = r.Field(Of String)("HeaderTxt").Single(), _
.headerStyleWidth = r.Field(Of String)("HeaderStyleWidth").Single(), _
.dataFormatString = r.Field(Of String)("DataFormatTxt").Single() _
}
gridCol.SortExpression = From c In colSettingsQry _
Select c.sortExpression
gridCol.HeaderText = From c In colSettingsQry _
Select c.headerTxt
... etc.
I'm guessing there's something pretty obvious that I'm missing - anyone have suggestions?
Thanks in advance.
I think you're looking for this:
Dim colSettingsQry = ... (your query)
Dim setting = colSettingsQry.FirstOrDefault()
If setting IsNot Nothing Then
gridCol.SortExpression = setting.SortExpression
gridCol.HeaderText = setting.HeaderText
...
EndIf
By FirstOrDefault you take the first element of a sequence if there is any, else Nothing.