.GroupBy with .Take VB.Net - vb.net

I am interested in filtering some data on several different criteria. When I pull my list from the database, I currently have two .Where clauses, one .GroupBy, and one .Take. My issue is I want to take 3 from each group and add it to my list, which ultimately shouldn't be a grouped list.
Here is some code I've been working with:
Dim dateRangeEnd As New Date(Property.Year, 10, 31)
Dim configurableCount = 3
Dim unfilteredList = _repository.GetIEnumerableFromDB(param1, param2) _
.Where(Function(r) ({status1, status2, status3}) _
.Any(Function(x) r.status.HasValue AndAlso x = r.status.Value)) _
.Where(Function(r) r.StartDate.Value.Year = Property.Year AndAlso r.StartDate.Value < dateRangeEnd) _
.GroupBy(Function(r) r.StartDate.Value.Month) _
.Take(configurableCount)
How can I edit this so that I adding only the configurableCount from each month to my list?

You flatten a sequence of sequences with SelectMany.
.Where(Function(r) ({status1, status2, status3}) _
.Any(Function(x) r.status.HasValue AndAlso x = r.status.Value)) _
.Where(Function(r) r.StartDate.Value.Year = Property.Year AndAlso r.StartDate.Value < dateRangeEnd) _
.GroupBy(Function(r) r.StartDate.Value.Month) _
.SelectMany(Function(g) g.Take(configurableCount))

Related

Is it possible to create an extension method that can be used in an sql query?

I have an application that draws elements from a database when a search button is clicked. I need to update it so that I can find elements that have a specific Substring exactly 6 places into the string. For example, I need to find 111-2233-44-555 by looking for 33 at the 6th and 7th places. My first instinct was to create an extension method for the string class so that I could say something like this:
Dim example As String = "111-2233-44-555"
If example.HasYear(33) Then
'Do Something'
End If
And this worked perfectly. Here's the method I made:
Public Module StringExtensionMethods
''' <summary>
''' Finds the year in a competition number of format XXX-XXXX-XX-XXX
''' </summary>
''' <param name="pstrCompNum">The competition number to find the year in</param>
<Extension()>
Public Function HasYear(ByVal pstrCompNum As String, ByVal pstrCompYear As String) As Boolean
Try
Dim testString As String = pstrCompNum
Debug.Print(testString)
Dim testSubstring As String = testString.Substring(6, 2)
If testSubstring.Equals(pstrCompYear) Then
Return True
End If
Return False
Catch ex As Exception
Throw ex
End Try
End Function
End Module
But a problem arises when I try to use this method in an SQL query. Rightfully so, since HasYear() isn't related to SQL in any way whatsoever. Here's the query I want to execute:
Dim o = From c In myContext.Competitions.Include("CodeJusticeBranches").Include("CodeJusticeLocations").Include("CodeCompetitionTypes").Include("CodePositionTypes").Include("CompetitionPositions") _
Where (pstrCompNum Is Nothing OrElse c.comp_number = pstrCompNum) _
And (pstrCompYear Is Nothing OrElse c.comp_number.HasYear(strYear) = True) _
And (pstrCompTypeId Is Nothing OrElse c.CodeCompetitionTypes.code_ct_id = CInt(pstrCompTypeId)) _
And (pstrBranchId Is Nothing OrElse c.CodeJusticeBranches.code_branch_id = CInt(pstrBranchId)) _
And (pstrPosTypeId Is Nothing OrElse c.CodePositionTypes.code_pos_type_id = CInt(pstrPosTypeId)) _
Order By c.comp_number _
Select c
I get the error LINQ to Entities does not recognize the method 'Boolean HasYear (System.String, System.String)' method, and this method cannot be translated into a store expression.
So what I'm looking for is essentially a way to make an extension method that can be used in an SQL query. Any ideas?
Don't include custom functions in your LINQ. It must be convertable to SQL so your extension method doesn't qualify. Just execute your query with supported where clauses, then tack on your custom queries after.
Dim o = (
From c In myContext.Competitions.Include("CodeJusticeBranches").Include("CodeJusticeLocations").Include("CodeCompetitionTypes").Include("CodePositionTypes").Include("CompetitionPositions")
Where (pstrCompNum Is Nothing OrElse c.comp_number = pstrCompNum) _
And (pstrCompTypeId Is Nothing OrElse c.CodeCompetitionTypes.code_ct_id = CInt(pstrCompTypeId)) _
And (pstrBranchId Is Nothing OrElse c.CodeJusticeBranches.code_branch_id = CInt(pstrBranchId)) _
And (pstrPosTypeId Is Nothing OrElse c.CodePositionTypes.code_pos_type_id = CInt(pstrPosTypeId))
Order By c.comp_number Select c).ToList().
Where(Function(c) c.pstrCompYear Is Nothing OrElse c.comp_number.HasYear(strYear))
I like what #djv posted, but I've decided to try out Andrew Morton's suggestion, to just omit the HasYear() altogether. I ended up using this:
Dim k = From c In myContext.Competitions.Include("CodeJusticeBranches").Include("CodeJusticeLocations").Include("CodeCompetitionTypes").Include("CodePositionTypes").Include("CompetitionPositions") _
Where (pstrCompNum Is Nothing OrElse c.comp_number = pstrCompNum) _
And (pstrCompYear Is Nothing OrElse c.comp_number.Substring(6, 2) = pstrCompYear) _
And (pstrCompTypeId Is Nothing OrElse c.CodeCompetitionTypes.code_ct_id = CInt(pstrCompTypeId)) _
And (pstrBranchId Is Nothing OrElse c.CodeJusticeBranches.code_branch_id = CInt(pstrBranchId)) _
And (pstrPosTypeId Is Nothing OrElse c.CodePositionTypes.code_pos_type_id = CInt(pstrPosTypeId)) _
Order By c.comp_number _
Select c
Notice the line And (pstrCompYear Is Nothing OrElse c.comp_number.Substring(6, 2) = pstrCompYear)
This solved my problem very simply.

Linq query to display unique set of names for current data set

I have a linq query that is used to display the list of requests from multiple users.And a Requestor can have multple requests(So the grid can have same Requestor multiple times). Now i am creating a dropdown list with unique Requestor that are displayed on the grid.(issue is i am not getting the distinct list but getting all the Requestor multiple times). Below is the linq query i am unsing.Can anyone suggest with correct linq query.
Dim rqstQry = From x In db.Request_vws _
Order By x.RequestID Descending _
Select x.RequestID,
Descr = x.Descr, _
RequestorName = String.Format("{0} {1}", x.FIRST_NAME, x.LAST_NAME), _
RelatedTask = GetTaskDescr(x.WorkID, x.TaskLabel, x.TaskDescr), _
RequestDescr = GetRequestDescr(x.RequestType), x.SubmitDttm, x.UpdatedDttm, _
x.ChangeDttm, _
x.MigrTimeStr, x.MigrApptTime, _
x.Requestor Ditinct
DataBind:
RequestorCB1.DataSource = rqstQry
RequestorCB1.DataTextField = "Requestor" RequestorCB1.DataValueField = "REquestor"
RequestorCB1.DataBind()
Need distinct user in the dropdownlist
Put parentheses around the LINQ query and append .Distinct()
Dim rqstQry = (From x In db.Request_vws _
Order By x.user
Select x.user).Distinct()
If you are including Request stuff in the result, then you cannot get distinct users (as Gert Arnold points out in his comment). Only include columns related to users.
If you still need information on requests then you must limit this information to one record per user. You would use a group by and use an aggregate in order to select a request (the first one, the last one etc.).
Got this by using the below query.
Dim rqstQry = (From x In db.Request_vws _
Order By x.RequestID Descending _
Select x.RequestID,
Descr = x.Descr, _
RequestorName = String.Format("{0} {1}", x.FIRST_NAME, x.LAST_NAME), _
RelatedTask = GetTaskDescr(x.WorkID, x.TaskLabel, x.TaskDescr), _
RequestDescr = GetRequestDescr(x.RequestType), x.SubmitDttm, x.UpdatedDttm, _
x.ChangeDttm, _
x.MigrTimeStr, x.MigrApptTime, _
x.Requestor). Distinct()
Dim rqstQry2 = (From y In rqstQry _
Select y.Requestor, y.RequestorName).Distinct()

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

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.