Trying to sort result obtained from Entity Framework - vb.net

Step 1: I am using entity to get the result. I run a query like this
Dim inbox = From p In dbContext.Inboxes Where p.RecordId = member_id Select p
Step 2: After that I put that into
Dim inboxList As IEnumerable(Of Entities.Inbox) = inbox.ToList()
so somewhere between step 1 and 2 I need to sort the List like so
inbox.OrderByDescending(Function(p) p.Importance) <-- PROBLEM HERE
The above line seems to have results, no exceptions, it just does not do anything!?
Any advice?

Order by will return a re-arranged value it won't actually rearrange itself.
You want:
inbox = inbox.OrderByDescending(Function(p) p.Importance)
Without assigning an output to a variable, it won't do anything

Related

Simplified way to find duplicates in list of objects in vb.net and merge them

I have an API that accepts a list of objects(Object contains ProductID and Quantity). I want to determine whether there are duplicate product ID on the passed list. If duplicates are found, I'll merge them by adding the Quantity of duplicate product ID. To do this, I created my own algorithm that loops the list one-by-one then store it to another list (the verified one). The code goes like this.
For Each passedVoucher As VoucherInfo In VouchersToRelease
indexofduplicate = 0
sumQuantity = 0
hasDuplicate = False
If VerifiedReleasedVouchers.Count() > 0 Then
'for loop that checks if productID exists in the VerifiedReleasedVouchers
For Each finalList As VoucherInfo In VerifiedReleasedVouchers
If passedVoucher.ProductID = finalList.ProductID Then
indexofduplicate = VerifiedReleasedVouchers.IndexOf(finalList)
'if true, adds the Quantity of duplicate to the existing quantity at the VerifiedReleasedVouchers
sumQuantity = Convert.ToInt32(VerifiedReleasedVouchers(indexofduplicate).Quantity) + Convert.ToInt32(passedVoucher.Quantity)
VerifiedReleasedVouchers(indexofduplicate).Quantity = sumQuantity.ToString
hasDuplicate = True
Exit For
End If
Next
End If
'adds the voucher to verified released voucher if no duplicate was found
If hasDuplicate = False Then
VerifiedReleasedVouchers.Add(passedVoucher)
End If
Next
So what I did is, I ForEach looped the passed list. Inside the loop, I compared the current object from the passedList into every object in the verifiedList(w/c is empty by default) to determine whether their are duplicate product ID. If no duplicate was found, i'll just add the current object to the verifiedList. If duplicate was found, ill just update the object from the verified list with the same ProductID by storing the sum of the Quantity of both objects.
The code above works perfectly as intended but the thing is, it performs a lot of tasks. Is there any way to simplify what I did above?
P.S. List.Distinct() is not a solution for this
You could use Linq to easily achieve what you want. First you must GroupBy ProductID and then Select all the groups with a sum of the quantity. Finally, we get a list:
Dim VerifiedReleasedVouchers As List(Of VoucherInfo) = VouchersToRelease.GroupBy(Function(x) x.ProductID).[Select](Function(y) New VoucherInfo() With {
.ProductID = y.Key,
.Quantity = y.Sum(Function(z) z.Quantity),
.Denom = y.First().Denom
}).ToList()

Comparing Elements across collections

I have the following models:
class Collection(models.Model):
...
class Record(models.Model):
collection = models.ForeignKey(Collection, related_name='records')
filename = models.CharField(max_length=256)
checksum = models.CharField(max_length=64)
class Meta:
unique_together = (('filename', 'collection'),)
I want to perform the following query:
For each filename of Record I want to know the Collections that:
Do not provide a Record with that filename
or that provide such a Record but has a differing checksum
I have in mind an output like that:
| C1 C2 C3 <- collections
-----------+------------
file-1.txt | x
file-2.txt | x
file-3.txt | ! ! !
file-4.txt | x ! !
file-5.txt | ! ! x
x = missing
! = different checksum
What I've com up so far is that I create a query for each Collection, excluding all filenames that are within this collection but exist in others.
for collection in collections:
other_collections = [c for c in collections if c is not collection]
results[collection] = qs.filter(collection__in=other_collections).exclude(
filename__in=qs.filter(
collection=collection
).values_list('filename', flat=True)
).order_by('filename').values_list('filename', flat=True)
This somewhat solves the first part of my question, but is rather quirky and requires post-processing to get to the format I desire. And, more importantly, it does not address the checksum comparison.
Is it possible to perform the two queries in one combined step to get the results in the format I described above?
The solution would not necessarily have to use the QuerySet APIs, a fallback to raw SQL is fine by me too.
It is not possible to write a SQL query that returns a variable number of columns, although you can achieve that effect if you wrap everything in an array or JSON object.
If you know the collections, you could write SQL like this:
SELECT r.filename,
(SELECT r.checksum = r2.checksum FROM records r2 WHERE r.filename = r2.filename AND r2.collection_id = 1) AS c1,
(SELECT r.checksum = r2.checksum FROM records r2 WHERE r.filename = r2.filename AND r2.collection_id = 2) AS c2,
...
FROM records r
WHERE r.collection_id = 1
GROUP BY r.filename, r.checksum
For each filename/collection pair, you will get NULL if the collection doesn't have the record, true if the collection has it with the right checksum, or false if the collection has it with a different checksum.
I include WHERE r.collection_id = 1 because otherwise for the checksum comparison, you have to answer "different from what?"

How to write this LINQ/EF query?

So I have an Entity Framework 5 model that includes a many-to-many relationship.
CategoryValues --< CourseCategoryValues >-- Courses
I have a LINQ query that selects every Course in the database. I would really like to modify it to only select Courses that belong to a specific CategoryValue. My attempt thus far has failed?
Can anyone help me figure this out?
This is what I have tried:
Using database As SiteDataContext = New SiteDataContext
database.Configuration.ProxyCreationEnabled = False
database.Courses.Include("Classes")
database.Courses.Include("CourseCategoryValues")
query = (From c In database.Courses Select c Order By c.Name).Where(
Function(c) 0 < c.Classes.Where(Function([class]) [class].Status.ToLower = "open").Count
).Include(Function(r) r.Classes).Include(Function(r) r.CourseCategoryValues)
' Here is where I am trying to narrow down the query results
If (pid.HasValue) AndAlso (0 <> pid.Value) Then
query.Where(Function(c) c.CourseCategoryValues.Any(Function(v) v.CategoryValue.CategoryValueID = pid))
End If
model.PageData = query.ToList
End Using
I think you are only missing the assignment of the filter to the query variable. Where returns a new queryable, it doesn't modify the queryable you apply the Where to. So, you would need:
query = query.Where(...)
The Where expression itself looks correct to me.

I'm looking for a LINQ solution to create a sub-list where items w/ duplicate property values are skipped

The problem is: I have a list of objects, with some containing the same PlanId property value. I want to only grab the first occurrence of those and ignore the next object with that PlanId. The root problem is a View in the database, but it's tied in everywhere and I don't know if changing it will break a ton of stuff nearing a deadline, so I'm tossing in a hack for now.
So, if I have a list of PlanObjects like such.
Plan1.PlanId = 1
Plan2.PlanId = 1
Plan3.PlanId = 2
Plan4.PlanId = 3
Plan5.PlanId = 4
Plan6.PlanId = 4
I want to take a sub-list from that with LINQ (italics mean an item is not included)
Plan1.PlanId = 1
Plan2.PlanId = 1
Plan3.PlanId = 2
Plan4.PlanId = 3
Plan5.PlanId = 4
Plan6.PlanId = 4
For my needs, it doesn't matter which one is taken first. The Id is used to update a datbase record.
If I didn't explain that well enough, let me know and I'll edit the question. I think it makes sense though.
PlanObjects.GroupBy(p => p.PlanId).Select(r => r.First());
The other answer (and its comments) supplies the fluent interface solution. Here's the query syntax:
From p In PlanObjects Group By p.PlanId Into First Select First

Order By on a modified Entity Framework object

I'm getting an Entity Framework object collection from my Products table called oProducts. I am looping through the collection and setting the Quantity field from a separate function. Before I write out a display of the objects in HTML, I want to sort them by the Quantity. So, I am using LINQ to create a new collection and try to order from the object collection that I just modified. The modified values are definitely in the object collection and output properly, but the sort ordering isn't working on the modified field. Does anyone know what I'm overlooking or a better way to attempt this?
Dim oProducts = From p in ctx.Products _
Where p.Active = True _
Select p
For Each prod in oProducts
prod.Quantity = GetQuantity(prod.ProductID)
Next
Dim oOrderedProducts = From p in oProducts _
Order By p.Quantity Ascending _
Select p
For Each prod in oOrderedProducts
Response.Write(prod.Quantity.ToString())
'*** The Quantities are stored in the collection properly but it doesn't order by the Quantity field.
Next
There a few things you need to remember:
Doing From p in ctx.Products _ will always select from the database.
LINQ is lazy. It will not perform the database select until you iterate, and if you iterate twice, it will do the database select twice.
How data read from the database is combined with data already in the ObjectContext will vary based on the MergeOption of the query you execute. But it will always happen after the SQL is executed.
With those ideas in mind, let's consider your code:
This bit (1) creates an IQueryable<Product> which can be used as a basis for iteration or defining other queries. Note that it does not actually execute a database query.
Dim oProducts = From p in ctx.Products _ // (1)
Where p.Active = True _
Select p
The next bit (2) iterates the IQueryable defined above.
For Each prod in oProducts // (2)
prod.Quantity = GetQuantity(prod.ProductID)
Next
Doing so causes a selection from the database. Inside the loop, you mutate the object that's returned. In LINQ to Objects, this would have no effect whatsoever, because the next time you iterated the IQueryable it would be executed anew. However, Entity objects are special. Mutating them marks the entity is changed on the context. Calling SaveChanges() later would save those modifications to the database.
It's important to note, however, that you have not changed the IQueryable variable oProducts at all by doing this. It is still a reference to a query which will be executed every time it is iterated. It is not a list. If you want a list, you need to call ToList() or ToArray().
The following query (3) creates a new IQueryable based on the original oProducts. When iterated, this will produce new SQL which does much the same as the original query, except for the ordering you've added. Again, oProducts is a query, not a list.
Dim oOrderedProducts = From p in oProducts _ // 3
Order By p.Quantity Ascending _
Select p
Finally, your code executes a new database query based on the IQueryable you defined above. As it executes this query, it merges the information retrieved from the database with the knowledge about the mutation you did above from the object context. Because the ordering is based on the query you defined, it is performed on the database server, using information in the database. Because information from the database is merged with information from the object context, you still see the mutation you performed on the data above, in (2).
For Each prod in oOrderedProducts
Response.Write(prod.Quantity.ToString())
'*** The Quantities are stored in the collection properly but it doesn't order by the Quantity field.
Next
You don't need to use the 3 consecutive LINQ expressions, combine them into one, using projection, either into an anonymous type (as I did here) or a real one.
This, below, should work
var oProducts = From p in ctx.Products
Where p.Active == True
orderby p.Quantity Ascending
select new
{
Quantity = GetQuantity(p.ProductID)
// ... assign other properties too
}
foreach ( var prod in oProducts )
{
// do your output
}
How about this?
Dim oProducts = From p in ctx.Products _
Where p.Active = True _
Select p
For Each prod in oProducts
prod.Quantity = GetQuantity(prod.ProductID)
Next
Dim oOrderedProducts = oProducts.OrderBy(Function(p) p.Quantity)