iteration over two lists in EF notation - c#

I am using EF MVC.
In my model I have a User that I want to turn into a Student and I need to assign some fields to the Student. I am making a method to batch Users to Students and I need to assign some fields to the Student. This is what I am currently doing
var students = db.Students.AsQueryable();
var users = db.Users.Where(u => !students.Any(s => s.UserId == u.Id) ).ToList();//exclude current students from users
Student[] filteredStudents = new Student[users.Count];
var i = 0;
foreach (var user in users)
{
filteredStudents[i] = (Student)user;
filteredStudents[i].CreatedDate = DateTime.Now;
filteredStudents[i].ModifiedDate = DateTime.Now;
i++;
}
db.Students.AddRange(filteredStudents);
And in my Student class
public static explicit operator Student(User user)
{
Student student = new Student() { UserId = user.Id, ApplicationUser = user };
return student;
}
I feel like there should be something more elegant that I am missing.
I have seen zips but I wanted something that resembles EF notation, like this pseudo code
var finalList = students.AssignMany(UserId = Id, this.User = User, Users.Select(Id, User));
Is this possible?

By using ConvertAll() you can use your function that takes a user and returns a student and pass it as a parameter. The ConvertAll function will then return a list of Students.
var users = db.Users.Where(u => !students.Any(s => s.UserId == u.Id) ).ToList();//exclude current students from users
var newStudents = users.ConvertAll(new Converter<User, Student>(UserToStudent)); //newStudents will contain the converted users, to do with what you want.
......snip.....
function static Student UserToStudent(User toConvert){
Student student = new Student(){UserId = toConvert.Id, ApplicationUser = toConvert, CreatedDate = DateTime.Now, ModifiedDate = DateTime.Now};
return student;
}

Related

Swap Attribute in each item in a list c#

I have a list with items as follows:
public class Student {
public string Name;
public string Nickname;
}
var students = new List<Student> { new Student {Name = "Becca", Nickname = "Rebecca"},
new Student {Name = "Ray", Nickname = "Raymond"}}
If I want to swap Name and Nickname so that students would now be
var students = new List<Student> { new Student {Name = "Rebecca", Nickname = "Becca"},
new Student {Name = "Raymond", Nickname = "Ray"}}
How do I do that in linq?
You can use Linq's Select method to create a new instance of student with the values swapped.
var students = new List<Student>
{
new Student {Name = "Becca", Nickname = "Rebecca"},
new Student {Name = "Ray", Nickname = "Raymond"}
}
var swapped = students.Select(x => new Student {Name = x.Nickname, Nickname = x.Name});
Whenever we ask how to do X with each item in a list, the answer is usually two things: How to do X, and how to do something with each item in a list. Once we see that, we can separate the two problems and start with how to do X. The second part, how to do anything with each item in a list, is much simpler.
In this case, how do we do X - create a new Student from an existing one, with the names swapped?
That could be a method like this:
Student SwapNames(Student student)
{
return new Student {Name = student.Nickname, Nickname = student.Name};
}
or simplified as
Student SwapNames(Student student) =>
new Student {Name = student.Nickname, Nickname = student.Name};
Having solved that, determining how to do it to to items in a list is easier.
var swappedStudents = new List<Student>();
foreach(var student in originalStudents)
{
swappedStudents.Add(SwapNames(student));
}
Or, using LINQ:
var swappedStudents = originalStudents.Select(student => SwapNames(student));
...or simplified:
var swappedStudents = originalStudents.Select(SwapNames);
These both produce an IEnumerable<Student>. If we wanted a List<Student> we could append:
var swappedStudents = originalStudents.Select(SwapNames).ToList();
In the examples so far, the method that does X - in this case, swapping the names, is in a separate function. That can be helpful for readability. But we can also do it inline with an anonymous function, like this:
var swappedStudents = originalStudents.Select(
student => new Student { Name = student.Nickname, Nickname = student.Name });
That's an anonymous function that takes a Student as an argument and returns a new Student with the names swapped.

How to get data only relating to the session of the staff members StaffID

I have figured out that to get the staff members from a company I use the following:
public ActionResult Index()
{
int staffID = (int)Session["StaffID"];
var staffRecord = db.Staffs.FirstOrDefault(staff => staff.StaffID == staffID);
var company = staffRecord.Company;
var staffForCompany = company.Staffs;
return View(staffForCompany);
}
and this gets the bookings for that staff/company:
public ActionResult Index()
{
int staffID = (int)Session["StaffID"];
var staffRecord = db.Staffs.FirstOrDefault(staff => staff.StaffID == staffID);
var company = staffRecord.Company;
var bookingsForCompany = company.Bookings;
return View(bookingsForCompany);
}
What I am wanting to know is how do I get data from a table that is not directly associated with the staff/company tables.
I am wanting the Customers that relate to the company of the staff member logged in.
See image here http://www.adamoxenhamsmith.co.uk/Uploads/download.jpg
From the ER diagram given in the image, it seems Company has 1..n bookings and each booking is tied to 1 Customer
This should work:
var customers = staffRecord.Company.Bookings.Select(b => b.Customer);
This should work:
var customers = db.Staffs
.Where(s => s.StaffID == staffId)
.Select(s => s.Company)
.SelectMany(c => c.Bookings)
.Select(bk => bk.Customer)
.ToList();

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();
}

Select properties for particular entities LINQ

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.

Search for matching objects in a list and assign values

My class
class student
{
public string studentid { get; set; }
public string groupid { get; set; }//Group id
}
My List
List<student> pupils = new List<student>();
here I select students with no group id
var studentsWithNoGroupId = from student in pupils
where student.groupid =="00"
select student;
I want to go through a for loop which runs number of times equal to studentsWithNoGroupId.Count and assign the group ids with some values I have. (Just showing how to assign each object will be enough).
How to do that?
Or do I have to change my linq for that?
Please somebody help me.
pupils.Where(p => p.groupid == "00")
.ToList()
.ForEach(
p => {
p.groupid = "whatever";
});
EDIT(after Hatsjoem hint, even more simple):
pupils.FindAll(p => p.groupid == "00")
.ForEach(p => {
p.groupid = "whatever";
});
You mean something like this?
var studentsWithNoGroupId =
from student in pupils
where student.groupid =="00"
select student;
foreach(var student in studentsWithNoGroupId)
{
student.groupid = "x";
}
Your line:
var studentsWithNoGroupId = from student in pupils
where student.groupid =="00"
select student;
creates a Linq query. This query does not execute immediately. To execute it, try the following:
var studentsWithNoGroupIdQuery = from student in pupils
where student.groupid =="00"
select student;
var studentsWithNoGroupId = studentsWithNoGroupIdQuery.ToList();
foreach (var student in studentsWithNoGroupId)
{
student.groupid = // Your logic here.
}
// Save pupils back to the database here.
The ToList() causes the query to execute and store the results in a List<student>. It is important to execute the query before the iteration, because you are changing the same variable groupid on which the query is being filtered.
I don't know if this is what you are looking for but I will give it a try:
var yourPupils = pupils.Where(p => p.groupid == "00")
.Select(s => new student() {
studentid = s.studentid,
groupid = "yourCustomAssignedId"
}).ToList();
For-loop version:
var numberOfPupils = pupils.Count();
for (int i = 0; i < numberOfPupils; i++)
{
if (pupils[i].groupid == "00")
{
pupils[i].groupid = "yourCustomAssignedId";
}
}

Categories

Resources