How do I get this ViewModel correct? - asp.net-mvc

Utilizing MVC 5 and Entity Framework I am trying to set up my a ViewModel for my Index view for a show of Employees and their goals.
I have an employee table, an employeeMap table (join table with payload) and a goal table.
There is a one-to-many relationship between Employee and EmployeeMap and between Goal and EmployeeMap.
I am a total newbie and getting stuck with an index view, which initially should display employees, and when one employee is selected should display the goals of the employees.
I can't get my index action right:
var viewModel = new EmployeeGoals();
viewModel.Employees = db.Employees
.Include(d => d.Department)
.Include(e => e.Position)
.Include(m => m.EmployeeMaps)
.Where(d => d.OrganizationID == oid && d.Department.ManagerID == currentUser.EmployeeID)
.OrderBy(d => d.HireDate);
if (id != null)
{
ViewBag.EmployeeID = id.Value;
viewModel.EmployeeMaps = viewModel.Employees.Where(e => e.ID == id.Value).Single().EmployeeMaps;
viewModel.Goals = viewModel.EmployeeMaps.Where(e => e.EmployeeID == ViewBag.EmployeeID).Select(e => e.Goals);
}
if (goalID != null)
{
ViewBag.GoalID = goalID.Value;
viewModel.Activities = viewModel.Goals.Where(
x => x.ID == goalID).Single().Activities;
}
return View(viewModel);
I do get the viewmodel.employees populated correctly, but not the viewmodel.goals
oh, and the viewmodel is:
public class EmployeeGoals
{
public IEnumerable<EmployeeMap> EmployeeMaps { get; set; }
public IEnumerable<Employee> Employees { get; set; }
public IEnumerable<Goal> Goals { get; set; }
}
A hint to help me past this would be great.
Thanks
Got it working using a different approach:
viewModel.Employees = db.Employees
.Include(d => d.Department)
.Include(e => e.Position)
.Include(m => m.EmployeeMaps)
.Where(d => d.OrganizationID == oid && d.Department.ManagerID == currentUser.EmployeeID)
.OrderBy(d => d.HireDate);
if (id != null)
{
ViewBag.EmployeeID = id.Value;
viewModel.EmployeeMaps = viewModel.Employees.Where(e => e.ID == id.Value).Single().EmployeeMaps;
viewModel.Goals = from g in db.Goals
join m in db.EmployeeMaps on g.ID equals m.GoalID
join e in db.Employees on m.EmployeeID equals e.ID
where m.EmployeeID == id.Value
select g;
}
couldn't figure out how to get a result using navigation properties, so ended up with above solution. It does the job, but am I hitting the database too often?? Also, ended up with a mix of LINQ syntax'es - should go with just one of them, I know :-/

You should use SelectMany:
viewModel.Goals = viewModel.EmployeeMaps
.Where(e => e.EmployeeID == ViewBag.EmployeeID)
.SelectMany(e => e.Goals);
because EmployeeMaps.Where() is an IEnumerable, so a Select produces IEnumerable<IEnumerable<Goal>>. SelectMany flattens this into IEnumerable<Goal>.

Related

Orderby clause not working with drop down list in mvc

I am populating a drop down list using Linq and the orderby clause doesn't seem to work.
public List<Hello> getManagers()
{
var que = (from man in db.Table1
where man.Role == "Manager"
orderby man.Name
select new Hello
{
Managers = man.Name
}).Distinct().ToList();
return que;
}
Controller Class:
public ActionResult Index()
{
rp = new RequestProcess();
ViewBag.ID = fillSelectedList("", "ID", rp);
ViewBag.Managers = fillSelectedList("", "Managers", rp);
return View(""); //View 1
}
public static List<SelectListItem> fillSelectedList(string selValue, string type, RequestProcess rp )
{
List<SelectListItem> list = new List<SelectListItem>();
SelectListItem obj = new SelectListItem();
if (type == "Managers") {
var tempList= rp.getManagers();
tempList.ForEach(x =>
{
obj = new SelectListItem();
obj.Text = x.Managers;
obj.Value = x.Managers;
obj.Selected = x.Managers == selValue ? true : false;
list.Add(obj);
});
}
return list;
}
I am still receiving an un-ordered list. Any fixes?
The result is not ordered, because method Distinct does not return ordered results. What you need to do instead is to first call Disctinct, and only then OrderBy:
var que = (from man in db.Table1
where man.Role == "Manager"
select new Hello
{
Managers = man.Name
}).Distinct() // <- First distinct ...
.OrderBy(x => x.Managers) // <- ... then order by
.ToList();
As mentioned in the answer above, you need to sort the result after Distinct().
Also note that you are mixing Lambda expression and LINQ to Entities Queries... you may want to consider choosing one of them for consistency (though there is no syntax error if you mix them). This is the same query using lambda expression:
var que = _context.Table1
.Where(m => m.Role == "Manager")
.Select(h => new Hello { Managers = h.Name })
.Distinct()
.OrderBy(o => o.Managers)
.ToList();

How to apply a filter condition in a entity including related entity

How would the conversion of this SQL query to LinqToEntity Syntax using Method Syntax?
SELECT Recurso.IdRecurso, Recurso.Codigo,
Recurso.Titulo, Recurso.Descripcion
FROM Recurso INNER JOIN
RecursoUsuario ON Recurso.IdRecurso = RecursoUsuario.IdRecurso
WHERE (RecursoUsuario.IdUsuario = 1)
I test next, but error in .Where(x => x.RecursoUsuario.IdUsuario == idUsuario)
public List<Recurso> RecursosPorUsuario(int idUsuario)
{
List<Recurso> ru =
bd.Recurso.Include("RecursoUsuario")
.Where(x => x.RecursoUsuario.IdUsuario == idUsuario);
return ru;
}
Could you helme please?
Disclaimer: I'm the owner of the project Entity Framework Plus
EF+ Query IncludeFilter feature allow filtering related entities.
public List<Recurso> RecursosPorUsuario(int idUsuario)
{
List<Recurso> ru = bd.Recurso
.IncludeFilter(x => x.RecursoUsuario
.Where(x => x.RecursoUsuario.IdUsuario == idUsuario))
.ToList();
return ru;
}
Wiki: EF+ Query IncludeFilter
Solution #2
Another technique is by using projection (which is what my library do under the hood)
public List<Recurso> RecursosPorUsuario(int idUsuario)
{
List<Recurso> ru = bd.Recurso
.Select(x = new {
Recurso = x,
RecursoUsuarios = x.RecursoUsuario.
.Where(x => x.RecursoUsuario.IdUsuario == idUsuario)
})
.ToList()
.Select(x => x.Recurso)
.ToList();
return ru;
}

Remove a list of objects and add new ones efficiently using Entity Framework

I'm taking into two multiselect lists into my Edit Action Post method along with a viewmodel. For each multiselect I want to loop through each object and remove what is not selected and add what is.
In this case, I'm adding and removing users from the project that are of a certain role type. Initially I was thinking db.SaveChages() on each iteration but that seems inefficient? Any suggestions for a better approach? Currently this doesn't work... Pardon if I'm way off base, this is my 4th week learning MVC. Thanks in advance!
// POST: Projects/Edit
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles = "Administrator")]
public ActionResult Edit(ProjectEditViewModel vm, int ProjectStatusId)
{
if (ModelState.IsValid)
{
var project = db.Project.Find(vm.ProjectId);
project.Name = vm.ProjectName;
project.ProjectStatusId = ProjectStatusId;
var users = db.Users.Where((u => u.Projects.Any(ui => ui.ProjectId == vm.ProjectId)));
var currentDevs = users.Where(u => u.Roles.Any(ur => ur.RoleId == db.Roles.FirstOrDefault(r => r.Name == "Developer").Id));
var currentPMs = users.Where(u => u.Roles.Any(ur => ur.RoleId == db.Roles.FirstOrDefault(r => r.Name == "Project_Manager").Id));
if (currentDevs != null)
{
foreach (var cd in currentDevs)
{
project.Users.Remove(cd);
}
}
if (currentPMs != null)
{
foreach (var cpm in currentPMs)
{
project.Users.Remove(cpm);
}
}
if (vm.SelectedDevs != null)
{
foreach (var dev in vm.SelectedDevs)
{
var developer = users.FirstOrDefault(a => a.DisplayName == dev);
project.Users.Add(developer);
}
}
if (vm.SelectedPMs != null)
{
foreach (var pm in vm.SelectedPMs)
{
var projMgr = users.FirstOrDefault(a => a.DisplayName == pm);
project.Users.Add(projMgr);
}
}
db.Entry(project).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Details", new { id = vm.ProjectId });
}
return View(vm);
}
Thought I'd swing back around and post my solution for 1 of the two role types. Same logic was applied to the other (1st solution to SO woot!!!)
var devRoleId = db.Roles.FirstOrDefault(r => r.Name == "Developer").Id;
var users = db.Users.ToList();
//currently assigned developers
var currentDevs = (from p in project.Users
where p.Roles.Any(r => r.RoleId == devRoleId)
select p).ToList();
// if the new list is null and old list is not null, remove the old list members
if (vm.SelectedDevs == null)
{
if(currentDevs != null)
{
foreach (var d in currentDevs)
{
project.Users.Remove(d);
}
}
}
//if the new list is not null
if (vm.SelectedDevs != null)
{
if (currentDevs == null) //if the old list is null, add the new list members
{
foreach(var nd in vm.SelectedDevs)
{
project.Users.Add(users.FirstOrDefault( u => u.DisplayName == nd));
}
}
else //if the old list is not null, compare each new list member to old and if its new list member is truely new, add them
{
foreach(var nd in vm.SelectedDevs)
{
if(!currentDevs.Any(cd => cd.DisplayName == nd))
project.Users.Add(users.FirstOrDefault( u => u.DisplayName == nd));
}
}
}
This should work for you. You don't want to remove all the users and re-add them each time. That's going to cause a lot of problems. Instead, you remove only the ones that have been de-selected, and then add only the ones that have been newly-selected (did not exist in the list before).
var devRoleId = db.Roles.FirstOrDefault(r => r.Name == "Developer").Id;
var pmRoleId = db.Roles.FirstOrDefault(r => r.Name == "Project_Manager").Id;
// Remove de-selected devs
project.Users.Where(u => u.RoleId == devRoleId && !vm.SelectedDevs.Contains(u.DisplayName))
.ToList().ForEach(u => project.Users.Remove(u));
// Add newly selected devs
var existingDevs = project.Users.Where(u => u.RoleId == devRoleId).Select(m => m.DisplayName);
db.Users.Where(u => vm.SelectedDevs.Exclude(existingDevs).Contains(u.DisplayName))
.ToList().ForEach(u => project.Users.Add(u));
// Remove de-selected PMs
project.Users.Where(u => u.RoleId == pmRoleId && !vm.SelectedPMs.Contains(u.DisplayName))
.ToList().ForEach(u => project.Users.Remove(u));
// Add newly selected PMs
var existingPMs = project.Users.Where(u => u.RoleId == pmRoleId).Select(m => m.DisplayName);
db.Users.Where(u => vm.SelectedPMs.Exclude(existingPMs).Contains(u.DisplayName))
.ToList().ForEach(u => project.Users.Add(u));

Problems adding new property to existing node with Neo4jClient

My beginner problems with using Neo4jClient unfortunately continues :) I have a simple class WorkEntity for which I have an update query:
public class WorkEntity
{
public string Id { get; set; }
public string Description { get; set; }
public string StartDate { get; set; }
public string EndDate { get; set; }
}
var query = graphClient.Cypher
.Match("(work:Work)", "(city:City)", "(profession:Profession)", "(company:Company)", "(oldCompany:Company)", "(oldProfession:Profession)", "(oldCity:City)",
"(user:User)-[r1]->work", "work-[r2]->oldProfession", "work-[r3]->oldCompany", "work-[r4]->oldCity")
.Where((WorkEntity work) => work.Id == model.Id)
.AndWhere((CityEntity city) => city.Id == model.CityId)
.AndWhere((CityEntity oldCity) => oldCity.Id == oldModel.CityId)
.AndWhere((ProfessionEntity profession) => profession.Id == model.ProfessionId)
.AndWhere((ProfessionEntity oldProfession) => oldProfession.Id == oldModel.ProfessionId)
.AndWhere((CompanyEntity company) => company.Id == model.CompanyId)
.AndWhere((CompanyEntity oldCompany) => oldCompany.Id == oldModel.CompanyId)
.AndWhere("type(r1) = 'CURRENT'")
.AndWhere("type(r2) = 'WORK_AS_PROFESSION'")
.AndWhere("type(r3) = 'WORK_AT_COMPANY'")
.AndWhere("type(r4) = 'WORK_IN_CITY'")
.Set("work = {updatedWork}")
.WithParam("updatedWork", updatedEntity);
//If Date has been set delete current relationships
if (oldModel.EndDate == DateTime.MinValue && model.EndDate > DateTime.MinValue)
{
query = query.Delete("r1");
}
if (oldModel.ProfessionId != model.ProfessionId)
{
query = query.Delete("r2")
.CreateUnique("work-[:WORK_AS_PROFESSION]->profession");
}
if (oldModel.CompanyId != model.CompanyId)
{
query = query.Delete("r3")
.CreateUnique("work-[:WORK_AT_COMPANY]->company");
}
if (oldModel.CityId != model.CityId)
{
query = query.Delete("r4")
.CreateUnique("work-[:WORK_IN_CITY]->city");
}
query.ExecuteWithoutResults();
Modifying all the relationsships works as expected but if for example Description is null from the beginning above query won't add Description to an existing entity (or update any other of the properties) even though it is set in updatedEntity. If I simplify the query as below Description is added/removed and existing changed properties are updated correctly. How can that be? I really would prefer to do the whole query in one transaction.
var query = graphClient.Cypher
.Match("(work:Work)")
.Where((WorkEntity work) => work.Id == model.Id)
.Set("work = {updatedWork}")
.WithParam("updatedWork", updatedEntity);
I think the reason the query does nothing when the CityIds are the same in my gist is because the MATCH clause:
work-[r4]-oldCity
is not matching anything, so the match fails and the query does nothing.
In effect - if I get this right - you're attempting two queries:
Update work
Delete & Create new relationships
I think you should check out the WITH keyword, as it should help your query...
WITH allows you to chain your cypher queries together, so you could change your query to:
var query = Client.Cypher
.Match("(work:Work)")
.Where((WorkEntity work) => work.Id == model.Id)
.Set("work = {updatedWork}")
.With("work")
.Match("(city:City)", "(profession:Profession)", "(company:Company)", "(oldCompany:Company)", "(oldProfession:Profession)", "(oldCity:City)",
"(user:User)-[r1]->work", "work-[r2]->oldProfession", "work-[r3]->oldCompany", "work-[r4]->oldCity")
.Where((WorkEntity work) => work.Id == model.Id)
.AndWhere((CityEntity city) => city.Id == model.CityId)
.AndWhere((CityEntity oldCity) => oldCity.Id == oldModel.CityId)
.AndWhere((ProfessionEntity profession) => profession.Id == model.ProfessionId)
.AndWhere((ProfessionEntity oldProfession) => oldProfession.Id == oldModel.ProfessionId)
.AndWhere((CompanyEntity company) => company.Id == model.CompanyId)
.AndWhere((CompanyEntity oldCompany) => oldCompany.Id == oldModel.CompanyId)
.AndWhere("type(r1) = 'CURRENT'")
.AndWhere("type(r2) = 'WORK_AS_PROFESSION'")
.AndWhere("type(r3) = 'WORK_AT_COMPANY'")
.AndWhere("type(r4) = 'WORK_IN_CITY'")
.WithParam("updatedWork", updatedEntity);
This will execute the update and then the rest.
Another keyword that might be worth a look would be MERGE but I think WITH should help you out here.
Thanks Chris, I realise that my understanding for Neo4j is still quite limited :) Your suggestion above updates the work entity properties correctly but not the relationsships. I don't really get why the work-[r4]-oldCity relationship doesn't match anything but anyway I took advantage of your note about this and after some trial and error finally got the query to work correctly like:
var oldModel = GetWorkModel(model.Id);
var updatedEntity = Mapper.Map<WorkEntity>(model);
var query = graphClient.Cypher
.Match("(work:Work)", "(city:City)", "(profession:Profession)", "(company:Company)",
"(user:User)-[r1:CURRENT]->work", "work-[r2:WORK_AS_PROFESSION]->()", "work-[r3:WORK_AT_COMPANY]->()", "work-[r4:WORK_IN_CITY]->()")
.Where((WorkEntity work) => work.Id == model.Id)
.AndWhere((CityEntity city) => city.Id == model.CityId)
.AndWhere((ProfessionEntity profession) => profession.Id == model.ProfessionId)
.AndWhere((CompanyEntity company) => company.Id == model.CompanyId)
.Set("work = {updatedWork}")
.WithParam("updatedWork", updatedEntity);
if (oldModel.EndDate == DateTime.MinValue && model.EndDate > DateTime.MinValue)
query = query.Delete("r1");
if (oldModel.ProfessionId != model.ProfessionId)
query = query.Delete("r2").CreateUnique("work-[:WORK_AS_PROFESSION]->profession");
if (oldModel.CompanyId != model.CompanyId)
query = query.Delete("r3").CreateUnique("work-[:WORK_AT_COMPANY]->company");
if (oldModel.CityId != model.CityId)
query = query.Delete("r4").CreateUnique("work-[:WORK_IN_CITY]->city");
query.ExecuteWithoutResults();

Some errors in controller (asp.net mvc)

I am getting some errors in my controller.
At first, I got Suppliers List, then I got Id for all Suppliers, then I got all Users for every Supplier.
public ActionResult Grid(bool? active)
{
var suppliers = Context.Suppliers.AsNoTracking()
.WhereIf(active != null, e => e.Active == active)
.Select(e => new SupplierRow
{
Id = e.Id,
FullName = e.FullName,
Active = e.Active,
Visits = e.Visits,
})
.ToList();
List<int> supplierIds = new List<int>();
foreach (SupplierRow sr in suppliers)
{
supplierIds.Add(sr.Id);
}
var users = Context.Users.AsNoTracking()
.Where(e => supplierIds.Contains(e.SupplierId))
.Select(e => new UserRow
{
Id = e.Id,
FullName = e.FullName,
Email = e.Email,
Name = e.Name,
Status = e.Status,
Role = e.Role,
SupplierId = e.SupplierId
}).toList();
foreach (UserRow ur in users)
{
foreach (SupplierRow sr in supplier)
{
if (ur.SupplierId == sr.Id)
{
sr.Users.Add(ur);
}
}
}
return PartialView("_Grid", suppliers);
}
here
and here
What's wrong with my code? How to fix that?
The problem is that you are trying to add Guid object to a collection that only accepts int values. Your User.SupplierId is an object of type Guid? (or Nullable<Guid>), while Supplier.Id is Guid. Fix the collection by declaring it as:
List<Guid> supplierIds = new List<Guid>();
Then in you code use:
foreach(SupplierRow sr in suppliers)
{
supplierIds.Add(sr.Id);
}
Do the same thing for users except that you will have to use SupplierId.HasValue and SupplierId.Value to check whether it has a value and to read the value. This is because it is declared as nullable Guid.

Resources