How can you take only the last result with Linq - c#

Ok, I didn't put the title right.
I'll explain.
I've got a table with some exercise results. Mostly you've got a UserID, an ExerciseID, the Result and when was the result taken. The table is accessed using EF and Linq.
I want to get all the results for a given ExerciseID and take only the last one of each User. Using LINQ of course.
How can this be accomplished?
My Code (that takes all results) is as follows:
from e in models.ExerciseUserResults
where e.ExerciseID == ExerciseId
select e
This snippet will get all results for all users.
EDIT: an example
Let's say I've got this table
{<UserID>,<Result>} - {1,100},{1,200},{2,150},{2,250} - using JSON notation and without all the fields.
If the query is right - I would get [{1, 200},{2,250}]
Thanks

The fact that you want one per user makes it pretty clear that you want to group the query by user. From there, you can project each group into just one item:
var query = from e in models.ExerciseUserResults
where e.ExerciseID == ExerciseId
group e by UserID into usersExercises
select usersExercises.Last();

Related

Multiple WHERE IN clause LINQ equivalent

I have a web page where the user can restrict results based on three different multi select choices (please see attached picture).
A bit of background. Documents have many sectors, companies and analysts assigned to them. My EF objects have navigation properties which means I don't need to write explicit JOINS to get the data I need.
My problem is I can't construct the LINQ query I need to get the results I need. Using SQL it would be simple and I could easily use a combination of JOINS and WHERE IN ('Blah', 'Another blah'). In my controller I have the following code:
public JsonResult FilterResearch(FilterResearchModel filterResearch)
{
var db = new Context();
// home model
var model = new HomeModel();
var selectedSectors = from s in db.Sectors
where filterResearch.SelectedSectors.Contains(s.Name)
select s;
var selectedCompanies = from c in db.Companies
where filterResearch.SelectedCompanies.Contains(c.Name)
select c;
var selectedAnalysts = from a in db.Analysts
where filterResearch.SelectedAnalysts.Contains(a.Name)
select a;
var filteredResults = from d in db.Documents
where selectedSectors.Contains(d.Sectors)
select d;
FilterResearch.Selected"Something" are string arrays. My "filteredResults" query is what should contain the filtered documents I plan to return.
EDIT
Some people have commented on the fact I'm not being clear. I'm trying to filter my documents based on 3 string arrays. Each of these string arrays are navigation properties in the "document" object. On the client the user has three multi select controls so each array can have more than one element. Now they can choose any of the three and choose as many options as they wish.
THIS IS THE PROBLEM When I compile it I get the following error: "cannot convert from 'System.Linq.IQueryable' to 'System.Linq.ParallelQuery>"
EDIT AGAIN Picture of error included. It occurs on "where selectedSectors.Contains(d.Sectors)"
I have checked:
Convert SQL to LINQ to Entities WHERE IN clause
LINQ to Entities - Where IN clause in query [duplicate]
Where IN clause in LINQ [duplicate]
with little luck. Is there a way where I can just say "filter the documents based on the companies AND sectors AND analysts?
Maybe I'm misunderstanding this (and I don't know your full object model) but wouldn't the following work...
var filteredResults = from d in db.Documents
where d.Sectors.Exists(sect => filterResearch.SelectedSectors.Contains(sect.Name))
&& d.Companies.Exists(comp => filterResearch.SelectedCompanies.Contains(comp.Name))
&& d.Analysts.Exists(anyst => filterResearch.SelectedAnalysts.Contains(anyst.Name))
select d;
Edit: Just to add, I haven't tried this in Visual Studio so it may not compile. Maybe you'd need to use a function other than Exists - Any perhaps - but I'm sure you get the gist of what I'm thinking.

Join in LINQ within a where clause

I have a database structure which has a set of users and their UserId
I then have a table called 'Post' which consists of a text field and a CreatedBy field.
I then have a 'Follows' table which consists of 'WhoIsFollowing' and 'WhoTheyFollow' fields.
The idea is that the 'Follows' table maps which users another user 'Follows'.
If I am using the application as a particular user and I want to get all my relevant 'Posts', these would be posts of those users I follow, or my own posts.
I have been trying to get this into one LINQ statement but have been failing to get it perfect. Ultimately I need to query the 'Posts' table for all the 'Posts' that I have posted, joined with all the posts of the people I follow in the 'Follows' table.
I have got it working with this statement
postsWeWant = (from s in db.Posts
join sa in db.Follows on s.CreatedBy equals sa.WhoTheyAreFollowing into joinTable1
from x in joinTable1.DefaultIfEmpty()
where (x.WhoIsFollowing == userId || s.CreatedBy == userId) && !s.Deleted
orderby s.DateCreated descending
select s).Take(25).ToList();
The issue is that it seems to come back with duplicates for all the posts posted by the user themselves. I have added .Distinct() to get around this, but instead of taking 25 posts each time, the duplicates are meaning it comes back with much less when there are a lot of posts by that user in the latest 25.
First off, why is the above coming back with duplicates? (It would help me understand the statement a bit more), and secondly how do I get around it?
Its difficult to say exactly without the data structure, but I would recommend investigating and perhaps expanding your join to eliminate duplicate association.
If that fails then I would use a group by clause to remove duplicates so there is no need for a distinct. The reason you are ending up with less than 25 records is probably because the elimination of duplicates is happening after taking 25. But I think I would need more of your code to tell for sure.

Using Contains() in a Where LINQ query

I'm using this piece of code to get a desired list of rows from a table:
_userObjectSet = EntityFrameWorkContext.CreateObjectSet<User>();
List<int> selectedUserIDs = Method(); //Returns a specific set of int user IDs...
var results = _userObjectSet.Where(c => selectedUserIDs.Contains(c.ID)).ToList();
This does work as 'results' will only contain records whose ID field matches an element in the selectedUserIDs list.
The problem is that, if I look at Windows Task Manager, LINQ seems to load ALL of the table's row THEN filter them out. There is a huge number of rows in this table, and pretty soon the process weights over 1GB, which I don't really like.
I can also tell that it's doing this because of the time it takes to complete.
Is there any way to tell LINQ to generate a query that would look like:
SELECT * FROM Users WHERE ID IN (34,55,66,77, etc.)
which would only return the exact rows I'm looking for and use less memory ?
Thanks!
Try join.. I think u can find a difference...
List<int> selectedUserIDs = Method(); //Returns a specific set of int user IDs...
var results = (from u in _userObjectSet
join id in selectedUserIDs on u.Id equals id
select u);
You're going to need something like LinqKit for this.Specifically, have a look at the PredicateBuilder that comes with the kit, as I think you need that to solve your problem.

Update in LINQ with one trip to database?

I'm having a hard time wrapping my head around when LINQ accesses the database, and how it knows when the optimal time would be. Mainly, this UPDATE statement seems to make two trips to the database. But it shouldn't -- there seems to be no "update" equivalent in LINQ short of querying for a row, updating it, and then saving that change. In basic SQL, I should be able to UPDATE WHERE in one fell swoop.
var query = from u in db.users where u.id == 1 select u;
foreach (user u in query)
{
u.name = "JOE";
}
db.Save();
I get the magic of Save. It allows me to make multiple changes before committing them. But there still seems to be the initial query for the rows.
Thoughts? Ideas?
EDIT
Now tested, I can confirm that it does in fact go to the database twice. There must be a better way!
If id is the primary key, you can use:
var u = new user { id = 1 };
db.users.Attach(u);
u.name = "JOE";
db.SubmitChanges();
Take a look at one of custom Linq extensions, like this one http://www.aneyfamily.com/terryandann/post/2008/04/Batch-Updates-and-Deletes-with-LINQ-to-SQL.aspx. You have DeleteBatch and UpdateBatch methods there. In particular, the UpdateBatch takes the condition and the updating expression and executes as one SQL query.

Linq is returning too many results when joined

In my schema I have two database tables. relationships and relationship_memberships. I am attempting to retrieve all the entries from the relationship table that have a specific member in it, thus having to join it with the relationship_memberships table. I have the following method in my business object:
public IList<DBMappings.relationships> GetRelationshipsByObjectId(int objId)
{
var results = from r in _context.Repository<DBMappings.relationships>()
join m in _context.Repository<DBMappings.relationship_memberships>()
on r.rel_id equals m.rel_id
where m.obj_id == objId
select r;
return results.ToList<DBMappings.relationships>();
}
_Context is my generic repository using code based on the code outlined here.
The problem is I have 3 records in the relationships table, and 3 records in the memberships table, each membership tied to a different relationship. 2 membership records have an obj_id value of 2 and the other is 3. I am trying to retrieve a list of all relationships related to object #2.
When this linq runs, _context.Repository<DBMappings.relationships>() returns the correct 3 records and _context.Repository<DBMappings.relationship_memberships>() returns 3 records. However, when the results.ToList() executes, the resulting list has 2 issues:
1) The resulting list contains 6 records, all of type DBMappings.relationships(). Upon further inspection there are 2 for each real relationship record, both are an exact copy of each other.
2) All relationships are returned, even if m.obj_id == 3, even though objId variable is correctly passed in as 2.
Can anyone see what's going on because I've spent 2 days looking at this code and I am unable to understand what is wrong. I have joins in other linq queries that seem to be working great, and my unit tests show that they are still working, so I must be doing something wrong with this. It seems like I need an extra pair of eyes on this one :)
Edit: Ok so it seems like the whole issue was the way I designed my unit test, since the unit test didn't actually assign ID values to the records since it wasn't hitting sql (for unit testing).
Marking the answer below as the answer though as I like the way he joins it all together better.
Just try like this
public IList<DBMappings.relationships> GetRelationshipsByObjectId(int objId)
{
var results = (from m in _context.Repository<DBMappings.relationship_memberships>()
where m.rel_id==objID
select m.relationships).ToList();
return results.ToList<DBMappings.relationships>();
}
How about to set _context.Log = Console.Out just to see the generated SQL query? Share the output with us (maybe use some streamwriter instead of console.out so that you can copy that easily and without mistakes).
Pz, the TaskConnect developer
I might have this backwards, but I don't think you need a join here. If you've setup your foreign keys correctly, this should work, right?
public IList<DBMappings.relationships> GetRelationshipsByObjectId(int objId)
{
var mems = _context.Repository<DBMappings.relationship_memberships>();
var results = mems.Where(m => m.obj_id == objId).Select(m => m.relationships);
return results.ToList<DBMappings.relationships>();
}
Here's the alternative (if I've reversed the mapping in my brain):
public IList<DBMappings.relationships> GetRelationshipsByObjectId(int objId)
{
var mems = _context.Repository<DBMappings.relationship_memberships>();
var results = mems.Where(m => m.obj_id == objId).SelectMany(m => m.relationships);
return results.ToList<DBMappings.relationships>();
}
Let me know if I'm way off with this, and I can take another stab at it.

Categories