Linq - Range variable hides a variable in an enclosing block - vb.net

I have several Linq statements in a public procedure (here are 2 of them):
'this one gets us table like this: "number of tickets, day"
Dim ticketsCreated = From ticket In CreatedTickets
Group ticket By GroupKey = New With {Key .IssueDate = CDate(ticket.Date).Date}
Into g = Group Select New
With {Key .IssuesCount = g.Count(), Key .IssueDate = GroupKey.IssueDate}
'this one gets us table like this: "number of tickets, day"
Dim ticketsClosed = From ticket In ClosedTickets
Group ticket By GroupKey = New With {Key .IssueDate = CDate(ticket.Date).Date}
Into g = Group Select New
With {Key .IssuesCount = g.Count(), Key .IssueDate = GroupKey.IssueDate}
I get the follwoing error on GroupKey in both statements:
Range variable hides a variable in an enclosing block, a previously defined range variable,
or an implicitly declared variable in a query expression
I do not have a variable named GroupKey declared anywhere outside of my Linq statements.

Related

VB.NET - List.Contains always returns false when it should be true

I am more of a C# developer, and I am stuck on trying to implement something on my visual basic code;
TL;DR is I have a database table which holds all the IDs of Employees that have gone through a process. I want to be able to get those into a list, and see if any of the current members going through this process exist on this list, and if they do remove those members so they don't get reprocessed. The database table returns an object called of type "ProcessedTermedMember" which is a custom object with three string propertys (ID, FirstName, LastName)
I basically have this code here, which is returning false... I know that it is looking to see the Reference is equal by default with contains, so it would only be true when it is the exact same object, vs an object where all properties are the same. How can I override this?
Here is my code:
Dim termedMembers = (From ec In db.ECs
Where ec.AccountTypeCode = "WCS"
Where ec.AccountStatus = 5
Select ec).ToList()
Dim processedTermMembers = (From p In db.ProcessedTermedMembers
Select p).ToList()
For Each member In termedMembers
Dim term As ProcessedTermedMember = New ProcessedTermedMember With {.EmployeeID = member.EmployeeID, .FirstName = member.EmployeeFirstName, .LastName = member.EmployeeLastName}
If processedTermMembers.Contains(term) Then
termedMembers.Remove(member)
End If
Next
This 'If Then' statement in the loop always returns "false" how can I get it to return true if all properties in the "term" variable are equal to the properties of one of the list items in "processedTermMembers"?
Any help would be greatly appreciated.
This won't work for a couple of reasons, but namely because you are attempting to modify a collection inside of a For/Each loop.
The simplest solution would be to move the declaration of termedMembers to after the declaration of processedTermMembers and to add an additional where clause to termedMembers:
Dim processedTermMembers = (From p In db.ProcessedTermedMembers
Select p).ToList()
Dim termedMembers = (From ec In db.ECs
Where ec.AccountTypeCode = "WCS" AndAlso ec.AccountStatus = 5 AndAlso Not processedTermMembers.Any(Function(term) term.EmployeeID = ec.EmployeeID AndAlso term.FirstName = ec.EmployeeFirstName AndAlso term.LastName = ec.EmployeeLastName)).ToList()
What the modified LINQ statement does is pull in the records from db.ECs where the AccountTypeCode is not "WCS", the AccountStatus is not 5, and is not in the processedTermMembers collection.
Just some modification into #David code :
Dim processedTermMembers = (From p In db.ProcessedTermedMembers
Select p).ToList()
Dim termedMembers = (From ec In db.ECs
Where (ec.AccountTypeCode = "WCS" _
AndAlso ec.AccountStatus = 5 _
AndAlso Not (processedTermMembers.Any(Function(term) ec.EmployeeID.Equals(term.EmployeeID) AndAlso ec.EmployeeFirstName.Equals(term.FirstName) AndAlso ec.EmployeeLastName.Equals(term.LastName))))).ToList()

Filter datatable with multiple column in where clause using lambda expression and VB.Net

I want to filter my datatable with multiple condition how to achieve using Lambda expression and VB.Net My sample code is as follow
Dim resultStyle = invData.Tables(0).AsEnumerable() _
.Where(Function(m) m.Field(Of Integer?)("InstitutionalInvestorStyleID").HasValue ) _
.GroupBy(Function(v) New With {Key .InvestorStyleID = v.Field(Of Integer)("InstitutionalInvestorStyleID"), Key .StyleName = v.Field(Of String)("InstitutionalInvestorStyleName")}) _
.Select(Function(v) New With {Key .InvestorStyleID = v.Key.InvestorStyleID, Key .StyleName = v.Key.StyleName, Key .Sum = v.Sum(Function(r) Double.Parse(r.Item("k001ICGeo").ToString()))})
i want to select only those data which "InstitutionalInvestorStyleID" not equal to null and with other column(Styleid column which can be multiple style id's)

Update property in collection with LINQ

I am new to Linq.
My actual code is this
Dim dt As DataTable = GetData()
Dim listOrder = From a In dt _
Group a By key = a("Name") Into g = Group _
Select Id = key, TotPoints = g.Sum(Function(r)r("Points"))
Order By TotPoints Descending
For Each item In listOrder
If item.Id = 1 Then
item.TotPoints = CalculateNewPoints()
End If
Next
The problem is that when trying to update a property receive the message: The property "xxxx" is ReadOnly
Thanks for any help!! and sorry for my bad English.
Yes, that should be. I believe that lista is a typo and it should be item. Here item iterator inside foreach is a readonly object reference and using which you can't perform any write operation like the way you are trying. Rather use a for loop.
For Each item In listOrder
If item.Id = 1 Then
item.TotPoints = CalculateNewPoints()
End If
You can as well use another LINQ query and then update the data like below but this would work only if Id is unique and there is only record with Id 1.
Itemtype item = listOrder.Single(x => x.Id == 1);
item.TotPoints = CalculateNewPoints();
You're creating instances of an anonymous type { Id, TotPoints }. Later on, you try to modify TotPoints. But anonymous types are immutable, or read-only, so you get this exception. The solution is to assign the right values at once:
Dim listOrder = From a In dt _
Group a By key = a("Name") Into g = Group _
Select _
Id = key, _
TotPoints = If (key = 1 _
, CalculateNewPoints() _
, g.Sum(Function(r)r("Points")))
Order By TotPoints Descending

Grouping by anonymous types in LINQ with non keyed values

I'm trying to group a LINQ query to find duplicate values. According to MSDN if i specify the properties as non-keyed it should ignore them when evaluating the expression:
From MSDN:
https://msdn.microsoft.com/en-us/library/bb384767.aspx
Dim prod7 = New With {Key .Name = "paperclips", Key .Price = 1.29,
.OnHand = 24}
Dim prod8 = New With {Key .Name = "paperclips", Key .Price = 1.29,
.OnHand = 423}
' The following line displays True, because prod7 and prod8 are
' instances of the same anonymous type, and the values of their
' key properties are equal. The equality check does not compare the
' values of the non-key field.
My code:
Dim query = people.GroupBy(Function(i) New With
{Key i.EmailAddress, Key i.LastName, Key i.FirstName, i.ID}).
Where(Function(g) g.Count() > 1).ToList()
I expect the code to return some results, it returns zero. If i remove i.ID i get the expected number of results.
The simple answer is LINQ's GroupBy function does not use the keyed properties. That said, this is what I believe you are looking for:
Dim query = people.GroupBy(Function(i) New With
{i.EmailAddress, i.LastName, i.FirstName}, Function(i) New With { i.ID}).
Where(Function(g) g.Count() > 1).ToList()
This version of GroupBy already has the Keyed properties and non-keyed properties separated out into separate lambda functions.
In fact, I'd go so far as to say, forget the Key properties entirely. I've never actually seen them used, and the implementation described in the MSDN article would be very counter intuitive. Two entities with only some properties being equal being considered equal is very odd behavior.

VB.NET Linq Left Join using Query Syntax

I have a list of applications. Each application may or may not have a support record associated with it.
The only way I can join is to take the App Title and see if its equal to the LookupValue of Support(Product). But to get that property, i have to cast to a FieldLookupValue. When there is no associated support record, that is where the null reference error gets thrown on .MoveNext() inside the Linq query. Below is what currently works for apps that have associated support, but throws the error when they don't.
Dim q =
From a In apps.AsEnumerable()
Group Join s In support.AsEnumerable()
On a("Title").ToString() Equals CType(s("Product"), FieldLookupValue).LookupValue
Into Group
From ajs In Group.DefaultIfEmpty()
Select New With {
.Name = a("Title"),
.SupportEnd = IIf(ajs Is Nothing, Nothing, ajs("End"))
}
Any way I could compare anonymous types in the On Statement? I can't seem to get the syntax right on that, or maybe its not possible. I feel that could fix the null reference error. My unsuccessful attempt returned Equals cannot compare type with the value of type
Dim q =
From a In apps.AsEnumerable()
Group Join s In support.AsEnumerable()
On
(New With {Key .AppName = a("Title").ToString()}) Equals
(New With {Key .AppName = IIf(s Is Nothing, "nada", CType(s("Product"), FieldLookupValue).LookupValue)})
Into Group
From ajs In Group.DefaultIfEmpty()
Select New With {
.Name = a("Title"),
.SupportEnd = IIf(ajs("End") Is Nothing, Nothing, ajs("End"))
}
Any ideas as to how to get this join to work using one of the two failed methods above would be great.
Once I created a dummy default object based on a random selection from the support list (i chose index 0), I used that to compare to as shown. Then just used the Let statement to have a boolean easily accessible when selecting.
Dim q =
From a In apps.AsEnumerable()
Group Join s In support.AsEnumerable()
On a("Title").ToString() Equals CType(s("Product"), FieldLookupValue).LookupValue
Into Group
From ajs In Group.DefaultIfEmpty(support(0))
Let NoSupport As Boolean = IIf(ajs.Equals(support(0)), True, False)
Select New With {
.Name = a("Title"),
.SupportEnd = IIf(NoSupport, "No Support", ajs("End"))
}