Select properties for particular entities LINQ - c#

You can probably see the result I want to get. It's easy using loop, but I can't understand how to achieve such result using LINQ extension methods
I have two contexts that target one DB. ApplicationUser is authentication class, and profileDTO profile info that I get from same DB.
ProfileDTO properties: string Id, string FirstName, string LastName
Both tables share same ID but are not connected neither through navigation properties nor any references in the DB.
IEnumerable<ViewModels.User.IndexViewModel> model;
IEnumerable<Models.ApplicationUser> users;
var profilesDtos = _profileService.GetAll();
using (var context = new Models.ApplicationDbContext())
{
users = context.Users.ToList();
}
model = users.Select(user =>
new ViewModels.User.IndexViewModel
{
Id = user.Id,
Email = user.Email,
PhoneNumber = user.PhoneNumber,
LockedOutTill = user.LockoutEndDateUtc ?? default(DateTime),
Roles = UserManager.GetRoles(user.Id)
});
foreach (var user in model)
{
var userProfile = profilesDtos.FirstOrDefault(o => o.Id == user.Id);
if (userProfile != null)
{
user.FirstName = userProfile.FirstName;
user.LastName = userProfile.LastName;
}
};
I want to get all users but with Names set only in those who have profiles.

You can use left join in Linq, like below -
IEnumerable<ViewModels.User.IndexViewModel> model;
IEnumerable<Models.ApplicationUser> users;
var profilesDtos = _profileService.GetAll();
using (var context = new Models.ApplicationDbContext())
{
users = context.Users.ToList();
}
model = (from u in users
join p in profilesDtos on u.Id equals p.Id into tempTbl
from up in tempTbl.DefaultIfEmpty()
select new ViewModels.User.IndexViewModel
{
Id = u.Id,
Email = u.Email,
PhoneNumber = u.PhoneNumber,
LockedOutTill = u.LockoutEndDateUtc ?? default(DateTime),
Roles = UserManager.GetRoles(u.Id),
FirstName = up!= null? up.FirstName : string.Empty;
LastName = up!= null? up.LastName : string.Empty;
}).ToList();

First of all I would suggest to update your context to setup such property. If you can't do this use JOIN:
var result =
from user in context.Users
join profile in userProfiles on user.ID equals profile.ID
select new ViewModels.User.IndexViewModel {
Id = user.Id,
FirstName = profile.FirstName,
...
}

As a solution, you can just join them.
MSDN
Plus DefaultIfEmpty statement.

Related

LINQ to Entities does not recognize the method 'System.Linq.IQueryable`..., and this method cannot be translated into a store expression

I have a method GetRoles to get the assigned roles for each user and I call this method in a IQueryable to make a RoleList. Every time I try to load the table, I get this error message below and I am not understanding as to what this error message means. Can someone please assist me in this issue, please?
LINQ to Entities does not recognize the method 'System.Linq.IQueryable`1[System.String] GetRoles(System.String)' method, and this method cannot be translated into a store expression.
GetRolesByUserId method
public static IQueryable<string> GetRoles(string userId)
{
var db = new ApplicationDbContext();
var userRoles = from identityUserRole in db.Set<IdentityUserRole>()
join identityRole in db.Set<IdentityRole>()
on identityUserRole.RoleId equals identityRole.Id
where identityUserRole.UserId == userId
select identityRole.Name;
return userRoles;
}
I am calling the above method in the one below (GetUsers)
private async Task<IQueryable<UserViewModel>> GetUsers(ApplicationDbContext db, IDbSet<ApplicationUser> users)
{
...........
var userlist = userlist.Join(db.ServicesUsers, u => u.Id, hu => hu.UserId, (u, hu) => new UserViewModel()
{
ID = u.Id,
Username = u.UserName,
FirstName = hu.FirstName,
LastName = hu.LastName,
Email = u.Email,
RoleList = GetRoles(u.Id),
Roles = "",
});
return hsuserlist;
But in the Error details page, this line is red as to showing this is the line with the error
List<UserViewModel> displayedTasks = filteredUsers.Skip(param.iDisplayStart).Take(param.iDisplayLength).ToList();
The above line is in the method (UsersGrid) that is displaying the table.
public async Task<ActionResult> UserGrid(DataTablesViewModel param)
{
using (var db = new Infrastructure.Data.ApplicationDbContext())
{
db.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
//var data = await GetUserData(db);
var users = db.Users;
var data = GetUsers(db, users);
IQueryable<UserViewModel> filteredUsers = data.Result;
..........
List<UserViewModel> displayedTasks = filteredUsers.Skip(param.iDisplayStart).Take(param.iDisplayLength).ToList();
}

Using Linq how do get a list of users and their last login from two tables

I'm trying to get a list of users and and their last login date.
I need to display the following columns
User Name
First Name
Last Name
Last Login Date
Role
Is Active
I'm having difficulty because the data is split between two tables:
User table:
Users
UserId
UserName
FirstName
LastName
Role
IsActive
And LogonHistory table
LogonHistory
Id
Username
LoginDate
I have tried using Join,group, and maxbut it only lets me use properties from the logonhistory table.
Here's an example of my joinquery:
var users = db.Users
.Join(db.LogonHistory, user => user.UserName, logon => logon.Username, (user, logon) => new UserSearchResults
{
UserName = user.UserName,
FirstName = user.FirstName,
LastName = user.LastName,
Email = user.Email,
IsActive = user.Active,
LoginHistory = logon.LoginDate
});
Here's an example of my grouping query:
var loginHistory = from l in db.LogonHistory
join u in db.Users on l.Username equals u.UserName
group l by l.Username into grp
let LastLoginDate = grp.Max(d => d.LoginDate)
from l in grp
where l.LoginDate == LastLoginDate
select l;
Can anyone spot what I'm doing wrong, or recommend a better method?
EDIT:
Essentially what I need to do is Join the User table and LogonHistory table on the Username and return the user's details and the latest login date.
var lastlogins = from h in db.LogonHistory
group h by h.UserName into hgroup
select new
{
UserName = hgroup.Key,
LastLoginDate = hgroup.Max(x => x.LoginDate)
};
var query = from u in db.Users
join h in lastlogins on u.UserName equals h.Username
select new
{
u.UserName,
u.FirstName,
u.LastName,
u.Role,
u.IsActive,
h.LastLoginDate
};
You should use navigation properties instead. Your query would be like:
var loginHistory= db.Users.Select(user => new UserSearchResults
{
UserName = user.UserName,
FirstName = user.FirstName,
LastName = user.LastName,
Email = user.Email,
IsActive = user.Active,
LastLoginDate=user.LogonHistory
.OrderByDescending(e=>e.LoginDate)
.FirstOrDefault().LoginDate
});
if you are using EntityFramework :
var loginHistory = db.Users.select(e => new {
e.UserName,
e.FirstName,
e.LastName,
e.Role,
e.IsActive,
e.LogonHistory.OrderByDescending(ee=>ee.LoginDate).FirstOrDefault().LoginDate
});

Sorting multiple LINQ results of different models as one list in MVC

I am trying to sort a list of users that are either students, colleagues or guests and sort them in my view based on their names.
Here is the code:
public ActionResult Index()
{
var db = new PraktikumDataContext();
var model = new List<AdminUserListItem>();
var studs = (from stud in db.Students select new AdminUserListItem() {Name = stud.FH_Angehörige.Name, LastLogin = stud.FH_Angehörige.FE_Nutzer.Letzter_Login, Rolle = "Student"}).OrderBy(stud => stud.Name);
model.AddRange(studs);
var mits = (from mit in db.Mitarbeiters select new AdminUserListItem() {Name = mit.FH_Angehörige.Name, LastLogin = mit.FH_Angehörige.FE_Nutzer.Letzter_Login, Rolle = "Mitarbeiter"}).OrderBy(stud => stud.Name);
model.AddRange(mits);
var gasts = (from gast in db.Gasts select new AdminUserListItem() {Name = gast.Name, LastLogin = gast.FE_Nutzer.Letzter_Login, Rolle = "Gast"}).OrderBy(stud => stud.Name);
model.AddRange(gasts);
model = model.OrderByDescending()
return View(model);
}
What I've already done with OrderBy sorts each model in it's own scope, however since I have 3 models, I am a little bit confused now how to somehow make them to be seen as one list and then sort them and show them in my website.
Consider using a LINQ union that makes a single call to the server:
public ActionResult Index()
{
var db = new PraktikumDataContext();
var model =
(from stud in db.Students
select new AdminUserListItem()
{
Name = stud.FH_Angehörige.Name,
LastLogin = stud.FH_Angehörige.FE_Nutzer.Letzter_Login,
Rolle = "Student"}
).Union(
from mit in db.Mitarbeiters
select new AdminUserListItem()
{
Name = mit.FH_Angehörige.Name,
LastLogin = mit.FH_Angehörige.FE_Nutzer.Letzter_Login,
Rolle = "Mitarbeiter"}
).Union(
from gast in db.Gasts
select new AdminUserListItem()
{
Name = gast.FH_Angehörige.Name,
LastLogin = gast.FE_Nutzer.Letzter_Login,
Rolle = "Gast"}
)
.OrderByDescending(a => a.Name)
.ToList();
return View(model);
}

How to join Enum with LINQ Query

I have user table (Default ApplicationUser Table from IdentityUser by ASP.CORE)
and I have added additional field for RoleType. There is also an Enum I added to specify Role Definition.
public enum Roles
{
Administrator = 1,
Headquarters = 2,
Branch = 3,
Driver = 4,
Client = 5
}
Now I want to show all the users in a view as a table along with role description.
I am unable to make LINQ query with Enum & User table using LINQ join.
To get the list of Roles from the enum use:
var roles = Enum.GetValues(typeof(Roles)).Cast<Roles>()
.Select(r => new { Value = (int)r, Name = r.ToString() }).ToList();
you can then use this in your Linq query, for example:
var roles = Enum.GetValues(typeof(Roles)).Cast<Roles>()
.Select(r => new { Value = (int)r, Name = r.ToString() }).ToList();
var users = from u in ApplicationUser
join r in roles on u.Role equals r.Value
select new {Name = u.Name, RoleId = u.Role, RoleDescription = r.Name} ;
A simpler way without the Enum.GetValues is:
var users = from u in ApplicationUser
select new {Name = u.Name, RoleId = u.Role, RoleDescription = (Roles)r.Role.ToString()} ;
var xx = from u in _context.Users
.Select(x => new ApplicationUserList
{ Firstname = x.Firstname,
RoleType = ((Roles)x.RoleId).ToString()
});
// This join is performed in memory
var results =
from e in Enum.GetValues(typeof(Roles)).Cast<Roles>()
join r in ApplicationUser on e equals r.Roles into rs
from r in rs.DefaultIfEmpty()
select new { Roles = e, Count = r?.Count ?? 0};
If I understand your question, you should first convert enum to dictionary an Join between what you need, here is an example:
static void Main(string[] args)
{
ApplicationUser a = new ApplicationUser();
a.userName = "a";
a.role = 1;
ApplicationUser b = new ApplicationUser();
b.userName = "b";
b.role = 3;
List<ApplicationUser> alist=new List<ApplicationUser>();
alist.Add(a);
alist.Add(b);
Dictionary<int, string> DicRoles = new Dictionary<int, string>();
var vals = Enum.GetValues(typeof(Roles));
foreach (var val in vals)
{
DicRoles.Add((int)val, val.ToString());
}
var result = from t in alist
join x in DicRoles on t.role equals x.Key
select new {t.userName,x.Value };
}
public enum Roles:int
{
Administrator = 1,
Headquarters = 2,
Branch = 3,
Driver = 4,
Client = 5
}
}
public class ApplicationUser
{
public string userName { get; set; }
public int role { get; set; }
}

Populate a view model with a data model with a method aswell

i need to populate my articles ViewModel with a model that has the database data in it, but i have a method that i need to assign to one of my properties
The list of images is the property that needs the method on it.
The method is called once for every item in the list of articles.
Here is my code:
public ActionResult ArticleTypes(string at)
{
articleViewModel.Images = new List<ImageInfo>();
var query = (from a in db.Articles
where a.SelectedArticleType == at
select new ArticlesViewModel
{
Id = a.Id,
Body = a.Body,
Headline = a.Headline,
PostedDate = a.PostedDate,
SelectedArticleType = a.SelectedArticleType,
UserName = a.UserName,
}).ToList();
articleViewModel.Images = imageService.GetImagesForArticle(articlemodel.Id.ToString());
return View(query);
}
I have also tried putting the method inside the linq:
public ActionResult ArticleTypes(string at)
{
articleViewModel.Images = new List<ImageInfo>();
var query = (from a in db.Articles
where a.SelectedArticleType == at
select new ArticlesViewModel
{
Id = a.Id,
Body = a.Body,
Headline = a.Headline,
PostedDate = a.PostedDate,
SelectedArticleType = a.SelectedArticleType,
UserName = a.UserName,
Images = imageService.GetImagesForArticle(a.Id.ToString())
}).ToList();
return View(query);
}
it throws an exception of:
An exception of type 'System.NotSupportedException' occurred in EntityFramework.SqlServer.dll but was not handled in user code
Additional information: LINQ to Entities does not recognize the method 'System.Collections.Generic.List`1[New_MinecraftNews_Webiste_MVC.Models.ImageInfo] GetImagesForArticle
I added a foreach loop at the end insted of anything else and it works:
public ActionResult ArticleTypes(string at)
{
articleViewModel.Images = new List<ImageInfo>();
var modelList = (from a in db.Articles
where a.SelectedArticleType == at
select new ArticlesViewModel
{
Id = a.Id,
Body = a.Body,
Headline = a.Headline,
PostedDate = a.PostedDate,
SelectedArticleType = a.SelectedArticleType,
UserName = a.UserName
}).ToList();
foreach (var model in modelList)
{
model.Images = imageService.GetImagesForArticle(model.Id.ToString());
}
return View(modelList);
}

Categories

Resources