My database currently exists of 2 tables. One is a user table, and the other is a followers table. Users can follow each other, which means the followers table will have a row that looks like this:
Table: followers
Row: user_id | follower_id
So if I have 3 users:
User 1 | id = 1
User 2 | id = 2
User 3 | id = 3
And user 1 follows user 2 and user 2 follows user 3, the rows in the followers table will look like this:
user_id = 2 | follower_id = 1
user_id = 3 | follower_id = 2
All of this works fine, but how should I create a query when I want to retrieve all followers from this user in c#? My class has a list of ids of people who follow this user. Like this: (This could be a list of user objects, I just need the ids for now)
public class User
{
public int id { get; set; }
public List<int> followers { get; set; }
}
I can retrieve the user from the database, I just can't figure out how to get the followers of this user all in one query. Any help would be appreciated!
The query could look like this:
SELECT u.id, u.name, GROUP_CONCAT(f.follower_id) AS list_of_followers
FROM user u
JOIN followers f ON u.id = f.user_id
WHERE u.id = 2
GROUP BY u.id, u.name
list_of_followers is just a comma-separated list of all followers of user with id=2. You can easily parse the string and populate a List<int> out of it.
Edit:
If you want to also get users with no followers, then you can modify the above query as follows:
SELECT u.id, u.name,
GROUP_CONCAT(f.follower_id) AS list_of_followers,
COUNT(f.follower_id) AS count_of_followers
FROM user u
LEFT JOIN followers f ON u.id = f.user_id
WHERE u.id = 2
GROUP BY u.id, u.name
For users with no followers count_of_followers = 0.
Related
Let's say I have two tables: Users and UserRoles:
Users:
+----+
| Id |
+----+
| 1 |
| 2 |
| 3 |
+----+
UserRoles:
+--------+----------+
| UserId | Name |
+--------+----------+
| 1 | A |
| 1 | B |
| 2 | A |
| 3 | C |
+--------+----------+
I need to be able to select above data into a list of User objects that looks like this:
class User
{
public int Id { get; set; }
public List<string> Roles { get; set; }
}
The best I could come up with is the following, but it ends up generating a nested select statement, and I don't know if that's going to hurt performance:
List<Device> usersWithRoles = from user in Users
select new Device
{
Id = user.Id,
Roles = (from role in UserRoles where role.UserId == user.Id select role.Name).ToList()
}.ToList();
Is there a better (more performant way) to accomplish this? For example with join, I was thinking something like this, but don't know how to populate roles from the join:
List<Device> usersWithRoles = from user in Users
join user_role in UserRoles on user.Id equals user_role.UserId
select new Device
{
Id = user.Id,
Roles = ??? // how do I populate this
}.ToList();
Thanks.
Use the GroupJoin LINQ to SQL method:
List<Device> usersWithRoles = from user in Users
join user_role in UserRoles on user.Id equals user_role.UserId into roles
select new Device
{
Id = user.Id,
Roles = roles.ToList()
}.ToList();
Let Users be a database containing typical users data (name, email...) with an ID as primary key.
Let Applications be a database storing a list of applications (name, developer...) with an ID as primary key.
Let UsersApps be the mapping table between the two, using the primary key. UsersApps thus stores rows of ..., {102, user1_Id, appA_Id}, {103, userN_ID, appB_Id}, {104, user1_Id, appC_Id}, ...
And so on.
I want to retrieve a list of users data {name, email, List<Application> boughtApps}
I am struggling to find a LINQ request to do that and I am trying to do it in two steps, get a big table and then build each user's list of applications.
var q1 = from user in _dbContext.Users
join appUti in _dbContext.AppUsers on user.Id equals appUti.UsersId
join app in _dbContext.Applications on appUti.ApplicationId equals app.Id
orderby user.Id
select new UserDataWithApp { Id = user.Id, Name = user.Name, firstName= user.FirstName, Email = user.Email, App = app };
Then parse q1.toList() to build my required results list.
var qRes = q1.ToList();
int i = 0;
int j = 0;
while (i<qRes.Count())
{
listUsersWithApps[j] = qRes[i];
while (qRes[i].Id == listUsersWithApps[j].Id) // llist is ordered !!
{
listUsersWithApps[j].Apps.Add(qRes[i].Apps[0]);
i++;
}
j++;
}
Isn't there a better way ?
You can use navigation properties to allow the following:
var userApps = context.Users.Select(u => new UserWithApp(u.Name, u.Email, u.Applications))
Just add to following to User:
public virtual ICollection<Application> Applications { get; set; }
and to Application:
public virtual ICollection<Users> Users { get; set; }
So you can "navigate" between your entities and write to following query (just adapt your ordering and what user data to be seleted):
var userApps = from user in context.Users
select new UserDataWithApp { ..., BoughtApps = user.Applications }
See here for an example: http://www.entityframeworktutorial.net/code-first/configure-many-to-many-relationship-in-code-first.aspx
or another interesting blog: https://lostechies.com/jimmybogard/2014/03/12/avoid-many-to-many-mappings-in-orms/
I've a LTSQL query which returns all users and userroles from SQL Server DB.
For example one user can have many user roles. So in the returned query if you look at a single user row it will have EntitySet(UserRole) for each userRole
The C# LTSQL query which returns all the the relational data is simply
var results = from u in db.Users
Select u;
Gridview.DataSource = results;
Gridview.Databind;
This displays all the data from the user table but not the userroles which is an entity in the results from the query.
If I want to display the user info from users table and all the userroles on a single gridView row, what is the most efficient approach?
The gridView should look something like this:
UserId | UserName | UserRoles |
1 | John Smith | Admin, Accountant | Edit
2 | Dave Jones | Sales, Admin | Edit
Something like this:
var results = from u in db.Users
Select new {
UserID = u.UsrID,
UserName = u.UserName,
UserRoles = string.Join(", ", u.UserRoles.Select(r => r.Name))
};
I have two tables:
User
-----------------
ID int
Name varchar
UserStatus
-----------------
ID int
UserId int
Status varchar
ActiveDate datetime
EndDate datetime
Using Linq in C# how would I get all information from the User table and all the columns from the user status table by the max active date and the end date being null?
I know the following wont work but it is kind of a rudimentary example of what I am trying to do.
ctx.Users.Include("UserStatus").Where(s => s.UserStatus.Max(t => t.ActiveDate) & s.UserStatus.EndDate != null).OrderBy(s => s.Name)
Thanks
EDIT
Lets say the user table has the following:
1 Billy Bob
2 Bilbo baggins
and the user status table has:
1 1 Active 1/1/2011 null
2 1 Reserved 1/1/2011 3/5/2011
3 2 Active 2/22/2011 null
I would like the following results:
1 Billy Bob Active 1/1/2011
2 Bilbo Baggins Active 2/22/2011
Try doing this in two parts. First, group by and set the max value (using the let keyword), then compare using that value and finally select your object.
var query = (from user in ctx.Users
join userStatus in ctx.UserStatuses on user.ID equals userStatus.User.ID
where userStatus.EndDate == null
group userStatus by new
{
userStatus.User.ID,
userStatus.ActiveDate
} into userGroup
let maxActiveDate = userGroup.Max(x => x.ActiveDate)
from userStatus in userGroup
where userStatus.ActiveDate == maxActiveDate
select new
{
userStatus.User.ID,
userStatus.User.Name,
userStatus.Status,
userStatus.ActiveDate
});
You could use something like this:
public IEnumerable<UserStatus> GetUsers(string name)
{
var query = (from u in db.User
join us in db.UserStatus on u.ID equals us.ID
where u.Name == name
orderby us.ActiveDate descending
select us)
as IEnumerable<UserStatus>;
return query;
}
Or, if you only want the single record w/ the MAX ActiveDate, you would keep the same linq statement, but use .First() at the end, and you'd change your return type as well to be only DateTime, etc.
ctx.Users.Where(u => u.UserStatus.Max(us => us.ActiveDate) && u.UserStatus.EndDate.HasValue())
.OrderBy(u => u.Name)
that seems like what you want
If you're looking for every entry that does NOT have an End Date (thus is active), then you could just use
Where(x => x.UserStatus.EndDate == null)
I don't see the need for the max date in your examples.
intHi,
Pretty new to LINQ.
I have a Ratings table.
A User adds a set of Ratings for an Entity
There can be more than one Rating set for an Entity per User.
For example, let's say the Entity is a car. The car is rated on Appearance and Performance. And a User can rate the given car's appearance and performance more than once. So my table looks something like this (the Rating field is not an Identity column; it is an int on a scale of 1 - 10):
ReviewID UserID EntityID CatID Rating Body DateSubmitted
1 3 6 1 7 "drives great" 8/01/2010 02:36:28 PM
2 3 6 2 8 "looks great" 8/01/2010 02:36:28 PM
3 3 6 1 2 "broke down" 8/18/2010 11:39:58 PM
4 3 6 2 1 "paint flaked off" 8/18/2010 11:39:58 PM
Now, I have a helper method where I supply the UserID and the EntityID and I want to return the most recent set of Ratings (into a ViewModel that includes the Rating Category).
public static IQueryable<RatingViewModel> GetRatingViewModel(int EntityID, int UserID)
{
DB _db = new DB();
var a =
from rating in _db.Ratings
join ratingCat in _db.RatingCategories
on rating.RatingCategoryID equals ratingCat.RatingCategoryID
where rating.UserID == UserID
&& rating.EntityID == EntityID
select new RatingViewModel
{
Rater = rating.User,
RaterRating = rating,
RatingCategory = ratingCat
};
return a;
}
What kind of "where" or "group by" or "order by" do I need to add to ONLY grab the most recent set of Ratings for the given UserID and EntityID?
Thanks!
Consider ordering by the DateSubmitted on the return of the method, and then taking the number of entries that you'd like.
var a = from rating in _db.Ratings
join ratingCat in _db.RatingCategories
on rating.RatingCategoryID equals ratingCat.RatingCategoryID
where rating.UserID == UserID
&& rating.EntityID == EntityID
orderby rating.DateSubmitted descending
select new RatingViewModel
{
Rater = rating.User,
RaterRating = rating,
RatingCategory = ratingCat
}
.Take(10);
.OrderByDescending(a => a.DateSubmitted).FirstOrDefault()