Asp.Net Core EF Core Table Joins - c#

I have two Objects, let says Employee and Address as follows
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public int DepartmentId {get; set; }
}
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
}
The relationship model is that each employee belongs to a single department (using DepartmentID property)
I want to create a view which shows all employees and instead of displaying the department ID, the department name is shown instead
My controller looks like this
public class EmployeeController : Controller
{
private readonly IAppRepository _appRepository;
public EmployeeController(IAppRepository appRepository)
{
_appRepository = appRepository;
}
public ViewResult List
{
Department department;
// retrieve all employee from DBContext (SQL Server)
var model = _appRepository.GetEmployees;
// retrieve department details
department = _appRepository.GetDepartment(model.DepartmentId);
ViewBag.department = department;
}
}
My problem seems to be with the last two lines (//retrieve department details. as model is returning an enumerable of employee.
How can this be achieved, I was thinking using ViewModel but have not found a way yet. thanks

In my opinion, you could configure relationships between Employee and Department,refer to here
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public int DepartmentId { get; set; }
[ForeignKey("DepartmentId")]
public Department Department { get; set; }
}
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
}
In your repository GetEmployees, you could get all employees with their own Department using Include, refer to here
var employees = _context.Employees.Include(e => e.Department).ToList();
Then your model of var model = _appRepository.GetEmployees; will contains Department info for each employee and you could achieve name in view,for example
#model IEnumerable<Employee>
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Department)
</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Department.Name)
</td>
</tr>
}
</tbody>
</table>

Related

How to show other model column in Razor view?

Currently i'm able to show all column in razer view from userRole model.
Just curious if i like to show the SiteName column in UserRole Razor view, instead of showing SiteID, is it possible? I know it can be done via custom view model, but is it a must? Please correct me if i'm wrong!
UserRole Model:
public int UserID { get; set; }
public string UserName { get; set; }
public int SiteID { get; set; }
*No SiteName column here....so i only can show SiteID in razor..
Site Model :
public int SiteID { get; set; }
public string SiteName { get; set; } <<-- the column i want..
Controller:
public ActionResult Index()
{
//need join table here perhaps?
return View(db.User_Role.ToList());
}
Razor View:
#model IEnumerable<FAB_Portal_POC.Models.UserRole>
<table class="table table-striped table-hover">
<tr>
<th>
#Html.DisplayNameFor(model => model.UserName)
</th>
<th>
#Html.DisplayNameFor(model => model.Role)
</th>
<th>
#Html.DisplayNameFor(model => model.SiteID)
</th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.UserName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Role)
</td>
<td>
#Html.DisplayFor(modelItem => item.SiteID) <<-- i want site name.
</td>
</tr>
}
</table>
The first problem is that your UserRole entity model doesn't seem to have a navigation property.
You don't need to, but then querying becomes awkward:
var rolesWithSite = from r in db.User_Role
join s in db.Sites on s.ID equals r.Site_ID
select new
{
Role = r,
Site = s
}
.ToList();
While when you add a navigation property (perhaps even instead of the foreign key property):
public class User_Role
{
public int UserID { get; set; }
public string Role { get; set; }
public string UserName { get; set; }
public virtual Site Site { get; set; }
}
Querying will become a whole lot easier:
var rolesWithSite = db.User_Role.Include(r => r.Site).ToList();
You can then introduce a viewmodel:
public class UserRoleViewModel
{
public int UserID { get; set; }
public string UserName { get; set; }
public string Role { get; set; }
public string Site { get; set; }
}
And map the query results to that:
var viewModels = rolesWithSite.Select(r => new UserRoleViewModel
{
UserID = r.UserID,
UserName = r.UserName,
Role = r.Role,
Site = r.Site.SiteName,
}).ToList();
return View(viewModels);
Entities and Models are very different.
You have to create a specific model for your page.
something like this :
PageModel
public string UserName { get; set; }
public string SiteName { get; set; }
In your controller your have to prepare all the data you want to send to the page. It s here that you will "associate" your entities to your pagemodel
public ActionResult Index()
{
var roles = db.User_Role.ToList();
var sites = db.Sites.ToList(); // i don t know how you get this information in your exemple.
//define here every property of the PageModel you want to use there
var model = new PageModel();
model.UserName = roles.UserName;
model.SiteName = sites.Select(...).SiteName;
return View(model);
}
then in razor view
#model IEnumerable<FAB_Portal_POC.Models.PageModel>
Hope it will help
You have multiple ways to do that the simple one is you have to create new class name UserRoleViewData and mention these fields and pass the class object to view. just like that.
New Class UserRoleViewData Look like this
public int UserID { get; set; }
public string UserName { get; set; }
public int SiteID { get; set; }
public string SiteName { get; set; }
and your Controller
public ActionResult Index()
{
List<className> dto = (from a in _db.User_Role
join b in db.Site on a.SiteID equals b.SiteID
select (new classname { UserID =a.UserID , UserName =a.UserName , SiteID =a.SiteID , SiteName =b.SiteName })).ToList();
return View(dto);
}
In View You have to change #model IEnumerable<FAB_Portal_POC.folderName.ClassName>
and map other property on view like others.I hope u'll understand and this will help you.
You can store the sites list in the ViewBag and show the site name by extracting it from the ViewBag:
public ActionResult Index()
{
ViewBag.Sites = db.Sites.ToList();
return View(db.User_Role.ToList());
}
And the View:
<td>
#Html.DisplayFor(modelItem => ViewBag.Sites.Single(s => s.SiteID == item.SiteID).SiteName)
</td>
Another, and to me a better approach, would be to create a ViewModel for your view which contains all of the data that you need. And return that view model to your view.
public class UserSiteViewModel
{
public int UserID { get; set; }
public string UserName { get; set; }
public string Role { get; set; }
public int SiteID { get; set; }
public string SiteName { get; set; }
}
No need to use ViewBag , join between 2 tables and store result in another viewModel it's vary bad idea. Please check out reference link, It will really help you to do Code in Very good direction. Click Here

MVC4 Foreign Key property is null

I have an Expenses and Categories tables. I have seeded the tables with few entries, but when pulled from DB the Category property is null. I would expect it to be automatically populated with entries from Category table. What am I missing?
Model:
public class Expense
{
public int ExpenseId { get; set; }
[DataType(DataType.Currency)]
public decimal Amount { get; set; }
[ForeignKey("CategoryId")]
public Category Category { get; set; }
[Required(ErrorMessage = "You must select some category!")]
public int CategoryId { get; set; }
}
public class Category
{
public int CategoryId { get; set; }
public string Description { get; set; }
public virtual ICollection<Expense> Expenses { get; set; }
}
View:
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Amount)
</td>
<td>
#item.CategoryId
</td>
<td>
#item.Category.Description //NullReferenceException here
</td>
</tr>
Controller:
// GET: Expenses
public ActionResult Index()
{
return View(db.Expenses.ToList());
}
Try marking the Category property as virtual. Navigation properties need to be virtual to support the lazy loading if it is not eager loaded.

Displaying information from another model on index

Noob warning - learning MVC for an academic project :-)
I have two models and I'm trying to show ProjectName (from first model) in my index view of Actors (from my second model). Everything loads up OK and I'm able to display projectId from my Actor class, but it's not picking up a value for #Html.DisplayFor(modelItem => item.project.ProjectName) in my index table.
However, it is picking up the correct [DisplayName("Project Name") in the table, which makes me think it is making some connection - just can't understand why it's not getting the value for it, I just have an empty column with a correctly labelled header!
I had planned on using a ViewModel to achieve this, but in the Wrox Professional ASP.NET MVC 4 book, the authors do what appears to be the same as what I am attempting in showing the Genre & Artist names (not ID's) in their Album view (MVC Music Store project - http://www.asp.net/mvc/overview/older-versions/mvc-music-store/mvc-music-store-part-4).
Help much appreciated!
public class Project
{
public int ID {get; set;}
[DisplayName ("Project Name")]
public string ProjectName { get; set; }
[DisplayName("Client")]
public string ClientID { get; set; }
public string Description { get; set; }
[DisplayName("Use Cases")]
public virtual ICollection <UseCase> UseCases { get; set; }
}
...
public class Actor
{
public int ID { get; set; }
public int projectID { get; set; }
public Project project { get; set; }
public string Title { get; set; }
[DataType(DataType.MultilineText)]
public string Description { get; set; }
}
Here is my controller code for the Index action (note, I'm passing the projectId in the URL and this is what I'm using as the parameter...
// GET: Actors
public ActionResult Index(int? id)
{
ViewBag.projectId = id;
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
else
{
return View(db.Actors.Where(x => x.projectID == id).ToList());
}
}
And here is the view code....
#model IEnumerable<JustSpecIt.Models.Actor>
#{
ViewBag.Title = "Actors";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Actors</h2>
<p>
#Html.ActionLink("Create New", "Create", new { id = ViewBag.projectId })
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.project.ProjectName)
</th>
<th>
#Html.DisplayNameFor(model => model.projectID)
</th>
<th>
#Html.DisplayNameFor(model => model.Title)
</th>
<th>
#Html.DisplayNameFor(model => model.Description)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.project.ProjectName)
</td>
<td>
#Html.DisplayFor(modelItem => item.projectID)
</td>
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
<td>
#Html.DisplayFor(modelItem => item.Description)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.ID }) |
#Html.ActionLink("Details", "Details", new { id=item.ID }) |
#Html.ActionLink("Delete", "Delete", new { id=item.ID })
</td>
</tr>
}
</table>
#Html.ActionLink("<< Back", "ShowSteps", "Projects", new { id = ViewBag.projectId }, null)
Try to add virtual keyword to project property in Actor class to allow lazy loading of the related Project
public class Actor
{
public int ID { get; set; }
public int projectID { get; set; }
public virtual Project project { get; set; }
public string Title { get; set; }
[DataType(DataType.MultilineText)]
public string Description { get; set; }
}
See here for more info regarding lazy loading related entities: http://msdn.microsoft.com/en-us/data/jj574232#lazy
public class Project
{
public int ID {get; set;}
[DisplayName ("Project Name")]
public string ProjectName { get; set; }
[DisplayName("Client")]
public string ClientID { get; set; }
public string Description { get; set; }
[DisplayName("Use Cases")]
public virtual ICollection <UseCase> UseCases { get; set; }
}
public class Actor
{
public int ID { get; set; }
public int projectID { get; set; }
public virtual Project project { get; set; }
public string Title { get; set; }
[DataType(DataType.MultilineText)]
public string Description { get; set; }
}
// GET: Actors
public ActionResult Index(int? id)
{
ViewBag.projectId = id;
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
using (var context = new DbContext()) // initiate a connection to the DB
{
var actors = context.Actors.Where(x => x.projectID == id);
// tell it to include the projects
actors.Include("project"); // the name of the property
// now retrieve list from the DB
actors.ToList();
// return the actors to the view
return View(actors);
}
}
So the DbContext is what ever you context is called. We wrap it in a using to let make sure we manage the connection properly.
The virtual keyword in front of Project project {get;set;} is to enable Lazy loading, which can be bad in some cases (Causes SELECT N + 1 problems). To make sure we include all the projects in our list of actors we use the Include function and pass the name of the property through.
To bypass issues with SELECT N + 1, we call the ToList() method before we pass the values to the view. If we don't then the view will execute the get actors SQL and for each actor it will execute a call to get the project. So if we have 10 actors, we will make 11 calls. Which isn't good. ToList() makes sure that we execute 1 call, that provides us with all the values we will need in the view.
Then we execute the DB call with ToList(). And pass the values back to the view.

MVC4 - How can I sum a result of a foreach list?

My project tracks vacation hours used per employee. I would like to display the Sum of hours used above the foreach result. The vacation index retireves the data for the selected employee.
Model 1
public class Employee
{
public int EmployeeID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public virtual ICollection<Vacation> Vacation { get; set; }
}
Model 2
public class Vacation
{
public int VacationID { get; set; }
public int EmployeeID { get; set; }
[DataType(DataType.Date)]
public DateTime EventDate { get; set; }
public int Hours { get; set; }
public virtual Employee Employee { get; set; }
}
Controller for Model 2
public class VacationController : Controller
{
private Context db = new Context();
//
// GET: /Vacation/
public ActionResult Index([Bind(Prefix = "id")]int employeeId)
{
var Employee = db.Employee.Find(employeeId);
if (Employee != null)
{
return View(Employee);
}
return HttpNotFound();
}
View
<table>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Employee.LastName)
</td>
<td>
#Html.DisplayFor(modelItem => item.EventDate)
</td>
<td>
#Html.DisplayFor(modelItem => item.Hours)
</td>
<td>
#Html.DisplayFor(modelItem => item.Notes)
</td>
</tr>
}
</table>
You can calculate the sum of hours with the following expression:
var sum = Model.Vacation.Sum(vacation => vacation.Hours);
You could add it at the top of your view:
Sum of hours: #Model.Vacation.Sum(vacation => vacation.Hours)
If you can set another field in the controller, you can sum the list before sending it to the view.
(Using LINQ extensions)
var totalHours = employee.Vacation.Aggregate((acc, val) => acc + val);

C# reflection use variable as object.[var]

I'm trying to generate a table in a razor view using reflection to pull the properties from the model.
Here is what I've tried:
#if (#Model.Count() > 0)
{
System.Reflection.PropertyInfo[] properties = Model.First().GetType().GetProperties();
<table>
<thead>
<tr>
#foreach (var property in properties)
{
if (char.IsLower(property.Name.ToCharArray()[0])) //ignore foreign keys
{
continue;
}
<th>#property.Name</th>
}
</tr>
</thead>
<tbody>
#foreach (PCNWeb.Models.Switch item in Model)
{
/*System.Reflection.PropertyInfo[]*/ properties = item.GetType().GetProperties();
<tr>
#foreach (var property in properties)
{
<td>
#Html.DisplayFor(modelItem => item.[property.Name])
</td>
}
</tr>
}
</tbody>
</table>
}
Let me point out the part of the code that I'm not sure what to do with:
<td>
#Html.DisplayFor(modelItem => item.[property.Name])
</td>
The property.Name contains the name of the property of item that I want to access.
If I were to hand write the innermost td one example would be:
<td>
#Html.DisplayFor(modelItem => item.Switch_Location)
</td>
where "Switch_Location" is the value of property.Name
So basically I need to access the value of a property of item based on the name of the property stored in a variable.
EDIT adding model:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace PCNWeb.Models
{
public partial class Switch
{
public Switch()
{
this.Ports = new List<Port>();
this.Switch_Location = new Switch_Location();
this.Switch_Model = new Switch_Model();
this.UPS = new UPS();
}
[Key]
public int switchRecId { get; set; }
[Required]
public int locationRecId { get; set; }
[Required]
public int modelRecId { get; set; }
//public int gatewayRecId { get; set; }
[Required]
public int upsRecId { get; set; }
[Required]
public int Number { get; set; }
[Required]
[StringLength(64)]
public string Name { get; set; }
[StringLength(80)]
public string Description { get; set; }
[StringLength(32)]
public string Cabinet { get; set; }
[StringLength(40)]
public string Power_Feed { get; set; }
[Required]
public Nullable<int> ipOctet1 { get; set; }
[Required]
public Nullable<int> ipOctet2 { get; set; }
[Required]
public Nullable<int> ipOctet3 { get; set; }
[Required]
public Nullable<int> ipOctet4 { get; set; }
public virtual ICollection<Port> Ports { get; set; }
public virtual Switch_Location Switch_Location { get; set; }
public virtual Switch_Model Switch_Model { get; set; }
public virtual UPS UPS { get; set; }
}
}
So basically I need to access the value of a property of item based on the name of the property stored in a variable.
No, you need to access the value of a property based on a PropertyInfo object describing it. That's far far easier.
property.GetValue(item)
If you dont really need the DisplayFor method, you can do it like this in your loop:
<tbody>
#foreach (PCNWeb.Models.Switch item in Model)
{
/*System.Reflection.PropertyInfo[]*/ properties = item.GetType().GetProperties();
<tr>
#foreach (var property in properties)
{
<td>
#property.GetValue(item,null)
</td>
}
</tr>
}
</tbody>

Categories

Resources