Linq, Joining a list to last item in another list - c#

I have a list of Users and another list of users activities.
I want to get list of users including user last activity.
I tried this:
var usersActivity =
from user in Users
join activity in Activities on user.Id equals activity.UserId
group activity by activity.UserId into UserActivities
select new {
activity=UserActivities.OrderByDescending(g=>g.DateTime).First(),
user=user
}
But it does not works. it says: The name 'user' does not exist in the current context
Am I wrong in joining or grouping?

Try grouping by user instead of user ID:
var usersActivity =
from user in Users
join activity in Activities on user.Id equals activity.UserId
group activity by user into UserActivities
select new
{
activity = UserActivities.OrderByDescending(g => g.DateTime).First(),
user = UserActivities.Key
};

You can use a simpler approach:
var usersActivity =
from user in Users
select new {
activity=Activities.Where(a => a.UserId = user.Id).OrderByDescending(a => a.DataTime).FirstOrDefault(),
user=user
}

Related

Problem with linq2sql Group-By then Where

I'm trying to get the user records that do NOT have the current role. A user can have multiple roles.
I'm working with 2 tables - Users and UserRoleMap
var superAdminRoleId = *GUID*
var query = from user in _userRepository.Table
join userRoleMap in _userRoleMapRepository.Table
on user.Id equals userRoleMap.UserId
// Now, group the roles by the user
group userRoleMap by user into userRoleMaps
// get the records that are NOT super admin
where !userRoleMaps.Any(map=>map.UserId == superAdminRoleId)
select userRoleMaps.Key;
I'm getting the error
LinqToDB.LinqToDBException: ''map.UserId' cannot be converted to SQL.'
So I revised it to
var query = from user in _userRepository.Table
join userRoleMap in _userRoleMapRepository.Table
on user.Id equals userRoleMap.UserId
// Now, group the roles by the user
group userRoleMap.UserId by user into userRoleMaps // changed userRoleMap to userRoleMap.UserId
// get the records that are NOT super admin
where !userRoleMaps.Any(map=>map == superAdminRoleId) // update
select userRoleMaps.Key;
Now, I'm getting
System.ArgumentException: 'Property 'System.Guid Id' is not defined for type 'System.Guid' (Parameter 'property')'
There's probably a way to fix your group by, but looking at what it seems you're trying to accomplish I think you'll get a better-performing and simpler query like this:
var userRoleMaps = _userRoleMapRepository.Table;
var nonAdminUsers = _userRepository.Table
.Where(user => !userRoleMaps
.Any(map => map.UserId == user.Id && map.RoleId == superAdminRoleId));

LINQ query to select specific data

I'm trying to write the query that will return only the specific information that I need to display in the view.
I have the following code:
var companiesWithResponsibleUser = from company in ctx.Companies
join user in ctx.Users on company.ResponsibleUserId equals user.Id
group new { company, user } by company.Id into g
select new
{
company = g.Select(t => t.company),
user = g.Select(t => t.user)
};
However this one returns the full data from both tables, which is very slow. I only need company name, address from company table and responsible user ID and full name from user table.
How can I rewrite this query so that it only populates these two objects with the data I want?
Following should help you:
var companiesWithResponsibleUser = from company in ctx.Companies
join user in ctx.Users on company.ResponsibleUserId equals user.Id
group new { company, user } by company.Id into g
select new
{
companyname = g.company.CompanyName
address= g.company.Address,
userid = g.user.UserId,
fullname= g.user.FullName
};
Please note that I haven't tested this.
I am not so sure but you can try this:
var companiesWithResponsibleUser = (from company in ctx.Companies
join user in ctx.Users on
company.ResponsibleUserId equals user.Id
select company.CompanyName,company.Address,user.Userid,user.UserFullName).ToList();
var Res = (from x in companiesWithResponsibleUser
group x by new
{
x.CompanyName,
x.Userid
} into g
select new {g.key.CompanyName, g.Address,g.key.Userid,g.UserFullName}).ToList();
You can add this part if error occur g.Address
x.CompanyName,
x.Userid,
x.Address
if field does not exists in select then user g.key.Address

Select all users that have commented post in ASP.NET MVC

I am having a trouble with LINQ query for sort of a complicated selection.
I am trying to create simply social network, where user can post posts and other users can comment on them.
For some statistic I want to find out who are the user who left comments on all posts of currently logged on user.
This are the tasks I am trying to preform:
Find currently logged on user.
Get all other registered user (except currently logged on).
Get all comments on all posts that currently logged on user has.
Since every comment has UserId which presents ID of a user who has left the comment, I want to compare this UserIds with all other registered users and get list of users who were commenting to currently logged on user.
It sound bit complicated, but it is not actually.
This is my code:
public ActionResult ListAllUsersThatCommentedPostsToCurrentUser()
{
ApplicationDbContext db = new ApplicationDbContext();
//Get ID from current user
var currentUserId = User.Identity.GetUserId();
var user = db.Users.SingleOrDefault(x => x.Id == currentUserId);
var comments = new List<Comment>();
if (user != null)
{
//Get all posts of current user
var postsOfCurrentUser = db.Posts.Where(x => x.UserId == user.Id).ToList();
foreach (var post in postsOfCurrentUser)
{
//Get all comments on posts which belong to current user
comments = db.Comments.Where(x => x.PostId == post.PostId).ToList();
}
}
var usersThatCommentedPosts = new List<ApplicationUser>();
if (comments != null)
{
//Get all user except current one
var otherUsers = db.Users.Where(u => u.Id != currentUserId).ToList();
foreach(var comment in comments)
{
//Filter all users except current one according to UserIds in Comment list
usersThatCommentedPosts = otherUsers.Where(u => u.Id == comment.UserId).ToList();
}
}
return View(usersThatCommentedPosts);
}
The problem is in this line:
usersThatCommentedPosts = otherUsers.Where(u => u.Id == comment.UserId).ToList();
I always get last user how left the comment to current user, instead of list of all users who have left comments.
I guess the problem is in foreach loop, but I have a mess in my head and no idea how can I implement this in a different and more efficient way.
As well, if somebody see something that can be refactored comments are more than welcome, because I have feeling that I have made unnecessary mess here.
you're replacing usersThatCommentedPosts every iteration of that loop. If you want to append to the loop, use AddRange
var users = otherUsers.Where(u => u.Id == comment.UserId).ToList();
usersThatCommentedPosts.AddRange(users );
or better yet, do a join in a proper query. (this way you don't have db queries in a loop)
var userQry =
from post in db.Posts
join comment in db.Comments on post.PostId equals comment.PostId
join otherUser in db.Users on comment.UserId equals otherUser.Id
where post.UserId == currentUserId
where otherUser.Id != currentUserId
select otherUser;
var users = userQry.ToList();

ASP .NET Identity 2.0 and LINQ compound query with group by

I am using ASP .NET Identity 2.0 as my user manager. Right now I want to retrieve the list of all users with RoleId different from "4". My LINQ query looks like this:
var userList = from entry in this.UserManager.Users
from roles in entry.Roles
where roles.RoleId != "4"
group entry by entry.Id into g
select new { g.Id, g.UserName, g.Email, g.EmailConfirmed, g.PhoneNumber, g.PhoneNumberConfirmed, g.Roles };
Unfortunately, group by completely doesn't work and g's parameters are not recognized. Without grouping the list contains separate rows for every user->role and it is not my desired effect. What is wrong in my query?
Edit:
Of course each user has a number of roles. If one of them is RoleId == 4 then I do not want his record to be retrieved.
I don't see any need for grouping. Try this much simpler query:
var userList = this.UserManager.Users
.Where(u => u.Roles.All(r => r.Id != "4"));
This should give you the result you are looking for.
var userList = (from entry in this.UserManager.Users
//group entry by entry.Id into g //I don't know why you need to group this,usually `id` is primary key
select new { entry.Id, entry.UserName, entry.Email, entry.EmailConfirmed, entry.PhoneNumber,
entry.PhoneNumberConfirmed, Roles=entry.Roles.Where(r=>r.RoleId != "4").ToList() })
.Where(g=>g.Roles.Count>0);
if there is no role's in result, the user will be removed

C# Insert missing records, from a list with linq?

I have an incomplete table of user settings. Now i have a small program that pulls settings from this table. How can i ensure that there is a record for that user in the database?
I got this list
List<Guid> usersWithoutInfo
This list contains the user ids that i need to pull the settings for. How can i first loop through this list and insert the missing records?
I am using a linq data context for all data handling.
Should be something like this. You'll have to tweak it a bit to compile and work for you
List<Guid> usersWithoutInfo = new List<Guid>(){......};
var usersToInsert = usersWithoutInfo.Where(userGuid => !usertableInDB.Contains(userGuid)).ToList();
Then use the usersToInsert to insert users in the database
I'd use the equivalent of SQL LEFT JOIN statement. Below statement will produce the list of users to check against existing ones paired with the existing user name. Those elements that have paired name as NULL need to be inserted:
List<string> users = new List<string> { "User1", "User3", "User5"};
List<string> usersToCheck = new List<string> { "User1", "User2", "User4", "User5" };
var leftJoin =
from userToCheck in usersToCheck
join existingUser in users
on userToCheck equals existingUser into joined
from existingUser in joined.DefaultIfEmpty()
select new
{
userToInsert = userToCheck,
user = existingUser ?? null
};
You may add WHERE before SELECT to filter out matching entries.
Use a simple foreach loop to iterate through your list, then use the Any keyword to see if any records for that Guid exist.
var newSettings = new List<SettingsTableRecord>();
foreach (var userGuid in usersWithoutInfo) {
if(!context.SettingsTableRecords.Any(u=>u.UserGuid.Equals(userGuid)){
//do work to crate records, such as
newSettings.Add(new SettingsTableRecord {UserGuid = userGuid, Email="Hi!"});
}
}
//insert the new records
context.SettingsTableRecords.InsertAllOnSubmit(newSettings);
context.SubmitChanges();
Substitute your table name for "SettingsTable" and your Guid field for "UserGuid", obviously.

Categories

Resources