How to show other model column in Razor view? - c#

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

Related

How to display value by selecting from model in the controller ASP MVC

I want to display a name in the razor page by select it from a model and pass it through Viewbag in the controller.
Controller
public IActionResult sheet()
{
var tp1 = _db.Topic.Where(t => t.sheet_id == 1).ToList();
var tp1name = tp1.Select(t => t.topic1name);
ViewBag.tp1name = tp1name;
return View();
}
Model
public class Topic
{
[Key]
public int topic_id { get; set; }
[Required]
public int sheet_id { get; set; }
[Required]
public string topic1name { get; set; }
[Required]
public string topic2name { get; set; }
}
public class Transaction
{
[Key]
public int id { get; set; }
[Required]
public int sheet_id { get; set; }
[Required]
public string topic1score { get; set; }
}
View page
#model transaction
<table class="table">
<tr>
<td rowspan="2">1</td>
<td rowspan="2">#ViewBag.tp1name</td>
<td rowspan="2">30</td>
<td>Below average</td>
<td>Average</td>
<td>Above average</td>
</tr>
It returns
System.Linq.Enumerable+SelectListIterator`2[UserLoginApp.Models.Topic,System.String] in the view page instead of topic1name
tp1 is a list of topics.
so when you do a select it creates a new Enumerable en for each item in tp1 it selects the value of topic1name.
Thus creating an Enumerable+SelectListIterator
I think you want the value of one item:
var tp1 = _db.Topic.FirstOrDefault(t => t.sheet_id == 1)
if(tp1 != null)
ViewBag.tp1name = tp1.topic1name;

The model item passed into the dictionary is of type 'System.Collections.Generic.List'1 requires a model item of type '.Generic.IEnumerable

I am trying to pull all the USERS created in the database(AspNetUsers) using Register form.
I have read several similar issues but can't fix this issue.Usually the reason for this error is when we try to pull a list from the controller but in view we do not specify IEnumerable at top.However I have added that as well , please see the code below. Million thanks for any help in advance.
#model IEnumerable<TimeSheet.ViewModel.UserViewModel>
#using TimeSheet.Models
#{
ViewBag.Title = "Index";
}
<h2>Manage User</h2>
<br/>
<table class="table table-condensed table-hover">
<tr class="table-header">
<th>
#Html.DisplayNameFor(m => m.Email)
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(m => item.Email)
</td>
<td>
#Html.Partial("_TableButtonPartial", new IndividualButtonPartial { UserId = item.Id })
</td>
</tr>
}
</table>
UserController
public class UserController : Controller
{
TimeSheetEntities db = new TimeSheetEntities();
/* private ApplicationDbContext db;
public UserController()
{
db = ApplicationDbContext.Create();
}*/
// GET: User
public ActionResult Index()
{
return View(db.AspNetUsers.ToList());
//return View();
}
}
UserViewModel
namespace TimeSheet.ViewModel
{
public class UserViewModel
{
[Required]
public string Id { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public string FullName { get { return this.FirstName + " " + this.LastName; } }
[Required]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[DataType(DataType.Password)]
public string Password { get; set; }
[DataType(DataType.Password)]
public string ConfirmPassword { get; set; }
public string UserRole { get; set; }
public string Manager { get; set; }
}
}
Since your are using mostly email,the easiest way to fix would be to change a model class
#model List<AspNetUser>
or if you want to save some network traffic, you can go a long way
create this viewmodel
public class UserGridViewModel
{
public string Id { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
}
fix the action
public ActionResult Index()
{
var model= db.AspNetUsers.Select(i=> new UserGridViewModel {
Id=i.Id,
Email=i.Email
}).ToList();
return View(model);
}
and view
#model List<TimeSheet.ViewModel.UserGridViewModel>

Asp.Net Core EF Core Table Joins

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>

passing string to controller/action from view

I am trying to lower the role of a user by clicking a link in my view.
When I click the link, it doesn't go to the action, but it just gives 404 error and links the resource that is not found with the string I am trying to pass to the action( referred to as "stringparameter")
In this case, the link is /Admin/Disable/stringparameter
I think I am not using the correct overload, so could someone help me out?
Thanks
This is the action in the AdminController
[HttpPost]
public ActionResult Disable(string id)
{
Role rol = new UserRepository().GetRole("Disabled");
new UserRepository().UpdateUser(id,rol.RoleId);
return RedirectToAction("Users");
}
this is the viewmodel
public class UserSuperUserPM
{
public UserClass User { get; set; }
public List<UserClass> Users { get; set; }
public UserClass SuperUser { get; set; }
public List<UserClass> SuperUsers { get; set; }
public UserClass Disabled { get; set; }
public List<UserClass> Disableds { get; set; }
public UserClass Inactive { get; set; }
public List<UserClass> Inactives { get; set; }
}
this is the userclass
public class UserClass
{
public string UserId { get; set; }
public string Username { get; set; }
public string Role { get; set; }
}
and this is the view(1 of the 4 similar tables in the view)
#foreach (var item in Model.Users)
{
<tr>
<td class="col-md-4">
#Html.DisplayFor(modelItem => item.Username, Model.Users)
</td>
<td class="col-md-4">
#Html.DisplayFor(modelItem => item.Role, Model.Users)
</td>
<td calss="col-md-4">
--------Commented attempted links(none of them work correct)
#*#Html.ActionLink("Disable", "Disable", new { Controller = "Admin", action = "Disable", id = item.UserId })*#
#*Disable*#
#*#Html.ActionLink("Disable","Admin", new { id = item.UserId },null)*#
#*#Html.ActionLink("Disable", "Disable","Admin", item.UserId)*#
-------original attempted link
#Html.ActionLink("Disable", "Disable", new { id = item.UserId})
</td>
</tr>
}
It's because href attr in a a element just do GET dont POST, so to it works change the Action to :
[HttpGet]
public ActionResult Disable(string id)
{
Role rol = new UserRepository().GetRole("Disabled");
new UserRepository().UpdateUser(id,rol.RoleId);
return RedirectToAction("Users");
}
But I suggest to you do this with JS.
Just delete [HttpPost] it says that this controller only can be access by POST method an you are trying to access by GET, you should let it post and do the call with AJAx

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.

Categories

Resources