How to write LINQ query? - c#

Above you can see FollowingUsers and StatusUpdates tables.
In FollowingUsers, I store Follower's Username and Following's Username.
In StatusUpdates, I store Status updates of users.
Below you can see original query I wrote to retrieve status updates of user who logged in.
var list = new List<StatusUpdate>();
list = (from x in db.StatusUpdates
where x.Author == User.Identity.Name
orderby x.Timestamp descending
select x)
.Take(count).ToList();
How to get status updates from followings of logged in user?

The following should work, although I don't have your database to test it on. Note that it won't actually be executed until the call to ToList so everything should still happen in a single database query. Also, the creation of a new list is not needed as it will be overwritten by your query so I've tidied that up a little.
var Users = from f in db.FollowingUsers
where f.FollowerId == User.Identity.Name
select f.FollowingId;
var list = (from x in db.StatusUpdates
from y in Users
where x.Author == y
orderby x.Timestamp descending
select x)
.Take(count).ToList();

Related

Equivalent LINQ query for SQL query not resulting in expected results

I am trying to write a LINQ query equivalent to below SQL
SELECT DISTINCT m.*,rm.RoleId FROM dbo.Menu m
INNER JOIN dbo.RoleMenu rm on m.Id=rm.MenuId
INNER JOIN dbo.RoleUser ru on rm.RoleId=ru.RoleId
WHERE ru.UserName='dd#dd.com' and m.Url='/dashboard#/pm'
I came with the below query which is not returning the expected output
var auth = _context.RoleUsers.Where(
x => x.Role.MenuRoles.FirstOrDefault().Menu.Url == pagePermissions.Url
&& x.UserName == pagePermissions.UserName).Count()
May I know a better way to do this?
Your sql looks at all the menus related to a role user, but your Linq is only looking at the first one. I think you want x.Role.MenuRoles.Any(mr => mr.Menu.Url == pagePermissions.Url). But then you're also doing a Count on the matching users instead of selecting the menus that match that url. A closer translation would be.
var results = (from m in _context.Menus
from rm in m.RoleMenus
from ru in rm.RoleUsers
where m.Url == pagePermissions.Url
&& u.UserName == pagePermissions.UserName
select new { Menu = m, rm.RoleId }).Distinct();
You may have to adjust some of the navigation properties as I was just guessing at them. They usually are pluralizations of the tables, but I see in your Linq that you have MenuRoles instead of RoleMenus.

Difficulty accessing data from twiitter using LinqToTwitter

I am using the following code in C#
var users = (from user in twitterCtx1.User
where user.Type == UserType.Lookup &&
user.UserID == list1
select user)
.ToList();
The list1 has all the IDs of the verified accounts of twitter and I am processing 75 records at a time. when I debug my code, I see that the list1 is populated with all the IDs but when the control passes in this portion of the code, it does not enter inside the it as the value being passed here is NULL
I am not able to understand as to why the value is NULL. What am I missing here?
Thanks in advance!
You're comparing user.UserID to the entire list1 object. Did you mean to write list1.Contains(user.userID)?
I'm still not clear on what error you're seeing, but here are some tips that might help.
list1 needs to be a string that is a comma-separated list of user IDS. Here's some code to show how it works:
var followers =
(from user in twitterCtx.SocialGraph
where user.Type == SocialGraphType.Followers &&
user.ScreenName == "JoeMayo"
select user)
.SingleOrDefault();
var userIDs = string.Join(",", followers.IDs.Take(100).ToList());
var users =
(from user in twitterCtx.User
where user.Type == UserType.Lookup &&
user.UserID == userIDs
select user)
.ToList();
First, the demo gets a list of user IDs. It's a user object with an IDs collection of type ulong. The next statement creates a comma-separated string of 100 of those IDs. Finally the lookup assigns that comma-separated string to the UserID property.
You should examine list1 and verify that it's a properly formatted comma-separated string of user IDs. The max size is 100.

When does this LINQ query go to the database?

I'm concerned that this LINQ call actually makes two trips to the database (once for Contains, once for ToList), when all I really want is the SQL-equivalent of a nested select statement:
var query1 = from y in e.cities where y.zip=12345 select y.Id;
var query2 = from x in e.users where query1.Contains(x.cityId) select x;
List<users> result = query2.ToList();
The point: If this is making a trip to the database twice, how do I avoid that? How can I have a nested select statement like this that will just execute as one query one time? Query1 will only ever return 1 or 0 rows. There must be a better way than using "Contains".
Since query1 and query2 are both IQueryable there is only one trip to the database - when you call query2.ToList()
You could combine the queries using a join since you are looking for related information and the relationship is that the user's city id is the same as the city you are restricting to:
var result = (from x in e.users
join y in e.cities
on x.cityId equals y.Id
where y.zip == 12345
select x.Id).ToList();
Above should give you a list of user ids of users that (presumably) live in the zip code 12345.

C# LINQ query (MYSQL EF) - Distinct and Latest Records

I have a table, lets call it Record. Containing:
ID (int) | CustID (int) | Time (datetime) | Data (varchar)
I need the latest (most recent) record for each customer:
SQL
select * from record as i group by i.custid having max(id);
LINQ version 1
dgvLatestDistinctRec.DataSource = from g in ee.Records
group g by g.CustID into grp
select grp.LastOrDefault();
This throws an error:
System.NotSupportedException was unhandled by user code Message=LINQ
to Entities does not recognize the method 'Faizan_Kazi_Utils.Record
LastOrDefault[Record
](System.Collections.Generic.IEnumerable`1[Faizan_Kazi_Utils.Record
])' method, and this method cannot be translated into a store
expression. Source=System.Data.Entity
LINQ version 2
var list = (from g in ee.Records
group g by g.CustID into grp
select grp).ToList();
Record[] list2 = (from grp in list
select grp.LastOrDefault()).ToArray();
dgvLatestDistinctRec.DataSource = list2;
This works, but is inefficient because it loads ALL records from the database into memory and then extracts just the last (most recent member) of each group.
Is there any LINQ solution that approaches the efficiency and readability of the mentioned SQL solution?
Update:
var results = (from rec in Record group rec by rec.CustID into grp
select new
{
CustID = grp.Key,
ID = grp.OrderByDescending(r => r.ID).Select(x => x.ID).FirstOrDefault(),
Data = grp.OrderByDescending(r => r.ID).Select(x => x.Data).FirstOrDefault()
}
);
So I made a test table and wrote a Linq -> SQL Query that will do exactly what you need. Take a look at this and let me know what you think. Only thing to keep in mind if this query is scaled I believe it will run a query to the DB for each and every CustID record after the grouping in the select new. The only way to be sure would be to run SQL Tracer when you run the query for info on that go here .. http://www.foliotek.com/devblog/tuning-sql-server-for-programmers/
Original:
Could you do something like this? from g in ee.Records where g.CustID == (from x in ee.Records where (g.CustID == x.CustID) && (g.ID == x.Max(ID)).Select(r => r.CustID))
That's all pseudo code but hopefully you get the idea.
I'm probably too late to help with your problem, but I had a similar issue and was able to get the desired results with a query like this:
from g in ee.Records
group g by g.CustID into grp
from last in (from custRec in grp where custRec.Id == grp.Max(cr => cr.Id) select custRec)
select last
What if you replace LastOrDefault() with simple Last()?
(Yes, you will have to check your records table isn't empty)
Because I can't see a way how MySQL can return you "Default" group. This is not the thing that can be simply translated to SQL.
I think grp.LastOrDefault(), a C# function, is something that SQL doesn't know about. LINQ turns your query into an SQL query for your db server to understand. You might want to try and create an stored procedure instead, or another way to filter out what your looking for.
The reason your second query works is because the LINQ to SQL returns a list and then you do a LINQ query (to filter out what you need) on a C# list, which implements the IEnumerable/IQueryable interfaces and understands the grp.LastOrDefault().
I had another idea:
// Get a list of all the id's i need by:
// grouping by CustID, and then selecting Max ID from each group.
var distinctLatest = (from x in ee.Records
group x by x.CustID into grp
select grp.Max(g => g.id)).ToArray();
// List<Record> result = new List<Record>();
//now we can retrieve individual records using the ID's retrieved above
// foreach (int i in distinctLatest)
// {
// var res = from g in ee.Records where g.id == i select g;
// var arr = res.ToArray();
// result.Add(res.First());
// }
// alternate version of foreach
dgvLatestDistinctRec.DataSource = from g in ee.Records
join i in distinctLatest
on g.id equals i
select g;

is there a better way to write this frankenstein LINQ query that searches for values in a child table and orders them by relevance?

I have a table of Users and a one to many UserSkills table. I need to be able to search for users based on skills. This query takes a list of desired skills and searches for users who have those skills. I want to sort the users based on the number of desired skills they posses. So if a users only has 1 of 3 desired skills he will be further down the list than the user who has 3 of 3 desired skills.
I start with my comma separated list of skill IDs that are being searched for:
List<short> searchedSkillsRaw = skills.Value.Split(',').Select(i => short.Parse(i)).ToList();
I then filter out only the types of users that are searchable:
List<User> users = (from u in db.Users
where
u.Verified == true &&
u.Level > 0 &&
u.Type == 1 &&
(u.UserDetail.City == city.SelectedValue || u.UserDetail.City == null)
select u).ToList();
and then comes the crazy part:
var fUsers = from u in users
select new
{
u.Id,
u.FirstName,
u.LastName,
u.UserName,
UserPhone = u.UserDetail.Phone,
UserSkills = (from uskills in u.UserSkills
join skillsJoin in configSkills on uskills.SkillId equals skillsJoin.ValueIdInt into tempSkills
from skillsJoin in tempSkills.DefaultIfEmpty()
where uskills.UserId == u.Id
select new
{
SkillId = uskills.SkillId,
SkillName = skillsJoin.Name,
SkillNameFound = searchedSkillsRaw.Contains(uskills.SkillId)
}),
UserSkillsFound = (from uskills in u.UserSkills
where uskills.UserId == u.Id && searchedSkillsRaw.Contains(uskills.SkillId)
select uskills.UserId).Count()
} into userResults
where userResults.UserSkillsFound > 0
orderby userResults.UserSkillsFound descending
select userResults;
and this works! But it seems super bloated and inefficient to me. Especially the secondary part that counts the number of skills found.
Thanks for any advice you can give.
--r
I think that should do the trick:
(from u in users
where u.UserSkills.Any(skill => searchedSkillsRaw.Contains(skill.SkillId))
select new
{
u.Id,
u.FirstName,
u.LastName,
u.UserName,
UserPhone = u.UserDetail.Phone,
UserSkills = u.UserSkills,
UserSkillsFound = u.UserSkills.Where(skill => searchedSkillsRaw.Contains(skill.SkillId)).Count()
} into userResults
orderby userResults.UserSkillsFound descending
select userResult).ToList();
However, since this is a query that gets executed on SQL server I strongly recommend to remove the 'ToList()' call from the first query. Because that actually causes LINQ to run two separate queries on the SQL server. You should change it to IQueryable instead. The power of LINQ is to construct queries in several steps without having to actually execute it in between. So 'ToList' should be called only at the end when the entire query has been constructed. In fact what you currently do is running the second query in memory rather than on the database server.
In regards to your UserSkills one-to-many relation you do not need to do an explicity join in LINQ. You can just access the collection property instead.
Let me know if you need more explanation.
Michael
Why not just let people do, say, fUsers.UserSkills.Count()? It would reduce the amount of data retrieved from the server in the first place.
Alternatively, you could create a View that has a calculated field in it and then map that to a type. Would push the query for count down into the DB.

Categories

Resources