I am getting the following error on the below line:
"Model reference not set to instance of an object"
<select asp-for="FilmGenre" asp-items="Model.Genres">
I am including the file below for reference.
Any advice would be greatly appreciated!
Thanks.
#model MvcFilm.Models.FilmGenreViewModel
<!-- allows you to access the list of movies that the controller passed to the view by using a Model object that's strongly typed.-->
<!-- Because the Model object is strongly typed (as an IEnumerable object), each item in the loop is typed as Film. -->
#{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<p>
<a asp-action="Create">Create New</a>
</p>
<form asp-controller="Films" asp-action="Index" method="get">
<p>
<select asp-for="FilmGenre" asp-items="Model.Genres">
<option value="">All</option>
</select>
Title:
<input type="text" asp-for="SearchString" />
<input type="submit" value="Filter" />
</p>
</form>
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.Films[0].Title)
</th>
<th>
#Html.DisplayNameFor(model => model.Films[0].ReleaseDate)
</th>
<th>
#Html.DisplayNameFor(model => model.Films[0].Genre)
</th>
<th>
#Html.DisplayNameFor(model => model.Films[0].Price)
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.Films)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Title)
</td>
<td>
#Html.DisplayFor(modelItem => item.ReleaseDate)
</td>
<td>
#Html.DisplayFor(modelItem => item.Genre)
</td>
<td>
#Html.DisplayFor(modelItem => item.Price)
</td>
<td>
<a asp-action="Edit" asp-route-id="#item.Id">Edit</a> |
<a asp-action="Details" asp-route-id="#item.Id">Details</a> |
<a asp-action="Delete" asp-route-id="#item.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
My model file: // FilmGenreViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Rendering;
using System.Collections.Generic;
namespace MvcFilm.Models
{
public class FilmGenreViewModel
// Films Genre view: Contains:
// - A list of films
// - A select list covering a list of genres.
// - A searchstring, which contains text that the users enter into the the search text box.
{
public List<Film> Films { get; set; }
public SelectList Genres { get; set; }
public string FilmGenre { get; set; }
public string SearchString { get; set; }
}
}
My controller file: FilmsController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using MvcFilm.Data;
using MvcFilm.Models;
namespace MvcFilm.Controllers
{
public class FilmsController : Controller
{
private readonly MvcFilmContext _context;
public object FilmGenreVM { get; private set; }
public FilmsController(MvcFilmContext context)
{
_context = context; // constructor uses dependency injection to inject the database context into the controller.
}
// GET: Films
public async Task<IActionResult> Index(string filmGenre, string searchString)
{
// Use LINQ to get a list of genres.
IQueryable<string> genreQuery = from m in _context.Film select m.Genre;
var films = from m in _context.Film
select m;
if (!String.IsNullOrEmpty(searchString))
{
films = films.Where(s => s.Title.Contains(searchString));
}
if (!string.IsNullOrEmpty(filmGenre))
{
films = films.Where(x => x.Genre == filmGenre);
}
var filmGenreVM = new FilmGenreViewModel
{
Genres = new SelectList(await genreQuery.Distinct().ToListAsync()),
Films = await films.ToListAsync()
};
return View(FilmGenreVM);
}
Object that passed to View from Controller method is null.
In the end of controller method should exists code like that:
var filmGenreViewModel = new FilmGenreViewModel();
//some code for initialize filmGenreViewModel may be here
return View(filmGenreViewModel);
As far as I know, if you want to use Select Tag helpers to render a SELECT element. You should set the view model's Genres type as List<SelectListItem>.
Like below:
public class CreateEmployeeViewModelData
{
//Other properties
public List<SelectListItem> Genres { set; get; }
}
Then you should build it in the controller method as below:
return View(new CreateEmployeeViewModelData {
Genres= new List<SelectListItem>
{
new SelectListItem {Text = "Shyju", Value = "1"},
new SelectListItem {Text = "Sean", Value = "2"}
}
);
}
Usage:
All
Result:
Besides, if the Genres is a list of class like below:
public class CreateEmployeeViewModelData
{
//Other properties
public List<Student> Genres { set; get; }
}
You could set it in controller like this:
return View(new CreateEmployeeViewModelData {
Genres= new List<Student>
{
new Student{Name = "Shyju", Id = "1"},
new Student{Name = "Sean", Id = "2"}
}
);
}
Then you could new SelectListItem in the select tag helper as below:
<select asp-for="FilmGenre" asp-items="#(new SelectList(Model.Genres,"Id","Name"))">
<option value="">All</option>
</select>
Realised the issue - it's a capitalization problem:
var filmGenreVM = new FilmGenreViewModel
{
Genres = new SelectList(await genreQuery.Distinct().ToListAsync()),
Films = await films.ToListAsync()
};
return View(FilmGenreVM);
}
Should be:
var filmGenreVM = new FilmGenreViewModel
{
Genres = new SelectList(await genreQuery.Distinct().ToListAsync()),
Films = await films.ToListAsync()
};
return View(filmGenreVM);
}
Related
I am making a page and on this page, there is a table. I want to filter Name from the table. And I wrote code like this:
Index.cshtml:
#using StudentApp.Models.Entity
#model List<StudentTable>
#{
ViewData["Title"] = "Manage Student";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<br />
<h1>Manage Student</h1>
<br />
#using (Html.BeginForm("Index", "Students", FormMethod.Get))
{
<p>
<b>Student Name:</b> #Html.TextBox("p");
<input type="submit" value="Ara">
</p>
}
<table id="tbl1" class="table table-bordered">
<tr>
<th>
Student ID
</th>
<th>
Student Name
</th>
<th>
Student Class
</th>
<th>
Edit
</th>
<th>
Delete
</th>
</tr>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#item.Id
</td>
<td>
#item.Name
</td>
<td>
#item.Class.ClassName // Error
</td>
<td>
Edit
</td>
<td>
Delete
</td>
</tr>
}
</tbody>
</table>
Add Student
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<link href="~/lib/bootstrap/dist/css/bootstrap.css" rel="stylesheet" />
<link href="https://cdn.datatables.net/1.10.15/css/dataTables.bootstrap.min.css" rel="stylesheet" />
<link href="https://cdn.datatables.net/responsive/2.1.1/css/responsive.bootstrap.min.css" rel="stylesheet" />
<script src="https://cdn.datatables.net/1.10.15/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.10.15/js/dataTables.bootstrap4.min.js "></script>
<script>
$('#tbl1').dataTable({});
</script>
StudentController.cs:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using StudentApp.Models.Entity;
using System.Linq;
namespace StudentApp.Controllers
{
public class StudentTableController : Controller
{
StudentDatabaseContext db = new StudentDatabaseContext();
public IActionResult Index(string p)
{
var degerler = from d in db.StudentTables select d;
if (!string.IsNullOrEmpty(p))
{
degerler = degerler.Where(m => m.Name.Contains(p));
}
return View(degerler.ToList());
}
[HttpGet]
public ActionResult AddStudent()
{
List<SelectListItem> GetClass = new List<SelectListItem>();
foreach (var item in db.ClassTables.ToList())
{
GetClass.Add(new SelectListItem { Text = item.ClassName, Value = item.Id.ToString() });
}
ViewBag.ClassList = GetClass;
return View(new StudentTable());
}
[HttpPost]
public ActionResult AddStudent(StudentTable st)
{
db.StudentTables.Add(st);
db.SaveChanges();
return RedirectToAction("Index");
}
public IActionResult Delete(int id)
{
var student = db.StudentTables.SingleOrDefault(i => i.Id == id);
db.StudentTables.Remove(student);
db.SaveChanges();
return RedirectToAction("Index");
}
[HttpGet]
public ActionResult EditStudent(int id)
{
var info = db.StudentTables.SingleOrDefault(i => i.Id == id);
List<SelectListItem> GetClass = new List<SelectListItem>();
foreach (var item in db.ClassTables.ToList())
{
GetClass.Add(new SelectListItem { Text = item.ClassName, Value = item.Id.ToString() });
}
ViewBag.ClassT = GetClass;
return View("EditStudent", info);
}
[HttpPost]
public ActionResult EditStudent(StudentTable p)
{
var StudentT = db.StudentTables.SingleOrDefault(i => i.Id == p.Id);
StudentT.Name = p.Name;
StudentT.ClassId = p.ClassId;
db.SaveChanges();
return RedirectToAction("Index");
}
}
}
StudentTable.cs:
using System;
using System.Collections.Generic;
namespace StudentApp.Models.Entity
{
public partial class StudentTable
{
public int Id { get; set; }
public string? Name { get; set; }
public int ClassId { get; set; }
public virtual ClassTable? Class { get; set; }
}
}
When I run the codes, I get the following error on the Index.cshtml page:
Why could this be? How can I fix the error? Thanks in advance for your help.
You need an eager loading for the StudentTable entities to load its related Class entities.
public IActionResult Index(string p)
{
...
return View(degerler
.Include(x => x.Class)
.ToList());
}
While Class property is nullable type in StudentTable, use ?. null-conditional operator to prevent accessing to inner property when the parent is null.
View
#item.Class?.ClassName
I am at a total loss. Everything seems correct. And when I look at the database, the correct number is being submitted. But when I go to list the data from the database, the Amount column in the database list is always the same number.
When you go to the deposit tab, the first number you put in is always the number that will be displayed. So if i enter $50, $50 will appear in the transaction tab. However, let's say if i go back and put $60. It will still say $50 in the transaction history tab, but in the database, it says $60. Why is it not displaying the number from the database?
Account controller:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using The_Bank_of_Cardinal.Areas.Identity.Data;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using The_Bank_of_Cardinal.Models;
namespace The_Bank_of_Cardinal.Controllers
{
[Authorize]
public class AccountController : Controller
{
private readonly TransactionConnection _tc;
private readonly UserManager<CardinalUser> userManager;
private readonly SignInManager<CardinalUser> signInManager;
private readonly DepositConnection _dc;
public AccountController(TransactionConnection tc, UserManager<CardinalUser> userManager, SignInManager<CardinalUser> signInManager, DepositConnection dc)
{
_tc = tc;
this.userManager = userManager;
this.signInManager = signInManager;
_dc = dc;
}
public IActionResult Index()
{
return View();
}
public IActionResult Transactions()
{
var results = _tc.TransactionHistory.ToList();
return View(results);
}
public IActionResult Test()
{
return View();
}
[HttpGet]
public IActionResult Deposit(string Id)
{
var resultss = _dc.AspNetUsers.Where(s => s.Id == Id).FirstOrDefault();
return View(resultss);
}
[HttpPost]
public IActionResult Deposit(DepositModel model, TransactionModel tm)
{
var resultss = _dc.AspNetUsers.Where(s => s.Id == model.Id).FirstOrDefault();
int test = model.AccountBalance + userManager.GetUserAsync(User).Result.AccountBalance;
tm.UserName = userManager.GetUserAsync(User).Result.UserName;
string name = tm.UserName;
tm.Description = "personal deposit";
tm.TransactionType = "Deposit";
tm.Amount = "$" + model.AccountBalance.ToString();
model.AccountBalance = test;
_tc.TransactionHistory.Add(tm);
_tc.SaveChanges();
_dc.AspNetUsers.Remove(resultss);
_dc.AspNetUsers.Add(model);
_dc.SaveChanges();
//_dc.AspNetUsers.
return Content("This is your info \n" +
$"Name: {name} \n" +
$"Description: {tm.Description} \n" +
$"type: {tm.TransactionType} \n" +
$"Amount {tm.Amount} \n");
}
public IActionResult Transfers()
{
return View();
}
}
}
Transaction view:
#model IEnumerable<The_Bank_of_Cardinal.Models.TransactionModel>
#{
ViewData["Title"] = "Transactions";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>Transactions</h1>
<p>
<a asp-action="Create">Create New</a>
</p>
#*<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.UserName)
</th>
<th>
#Html.DisplayNameFor(model => model.Description)
</th>
<th>
#Html.DisplayNameFor(model => model.TransactionType)
</th>
<th>
#Html.DisplayNameFor(model => model.Amount)
</th>
<th>
#Html.DisplayNameFor(model => model.Date)
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.UserName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Description)
</td>
<td>
#Html.DisplayFor(modelItem => item.TransactionType)
</td>
<td>
#Html.DisplayFor(modelItem => item.Amount)
</td>
<td>
#Html.DisplayFor(modelItem => item.Date)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
</td>
</tr>
}
</tbody>
</table>*#
<div class="container">
#if (Model != null)
{
<table class="table table-dark">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Description</th>
<th scope="col">Transaction Type</th>
<th scope="col">Amount</th>
<th scope="col">Date</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>#Html.DisplayFor(modelItem => item.UserName)</td>
<td>#Html.DisplayFor(modelItem => item.Description)</td>
<td>#Html.DisplayFor(modelItem => item.TransactionType)</td>
<td>#Html.DisplayFor(modelItem => item.Amount)</td>
<td>#Html.DisplayFor(modelItem => item.Date)</td>
</tr>
}
</tbody>
</table>
}
</div>
Deposit view:
#model The_Bank_of_Cardinal.Models.DepositModel
#{
ViewData["Title"] = "Deposit";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>Deposit</h1>
<h4>DepositModel</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Deposit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label hidden asp-for="Id" class="control-label"></label>
<input hidden asp-for="Id" class="form-control" />
<span asp-validation-for="Id" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="AccountBalance" class="control-label">Amount</label>
<input asp-for="AccountBalance" class="form-control" value="0" />
<span asp-validation-for="AccountBalance" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Deposit" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
Deposit model:
using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using The_Bank_of_Cardinal.Areas.Identity.Data;
namespace The_Bank_of_Cardinal.Models
{
public class DepositModel
{
[Key]
public string Id { get; set; }
public int AccountBalance { get; set; }
}
}
Transaction model:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
namespace The_Bank_of_Cardinal.Models
{
public class TransactionModel
{
[Key]
public string UserName { get; set; }
public string Description { get; set; }
public string TransactionType { get; set; }
public string Amount { get; set; }
public DateTime Date { get; set; }
}
}
Deposit DbContext:
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace The_Bank_of_Cardinal.Models
{
public class DepositConnection : DbContext
{
public DepositConnection(DbContextOptions<DepositConnection> options) : base(options)
{
}
public DbSet<DepositModel> AspNetUsers { get; set; }
}
}
Transaction DbContext:
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace The_Bank_of_Cardinal.Models
{
public class TransactionConnection : DbContext
{
public TransactionConnection(DbContextOptions<TransactionConnection> options) : base(options)
{
}
public DbSet<TransactionModel> TransactionHistory { get; set; }
}
}
The class TransactionModel has for primary key the property UserName.
But you have several TransactionModel instances with the same UserName value. This is contradictory. Each TransactionModel primary key must be unique.
Change your TransactionModel class to something like:
public class TransactionModel
{
public int Id { get; set; } // This is the primary key.
public string UserName { get; set; }
public string Description { get; set; }
public string TransactionType { get; set; }
public decimal Amount { get; set; }
public DateTime Date { get; set; }
}
The property Id is your primary key. It will be automatically incremented. This is by convention. See: https://learn.microsoft.com/en-us/ef/core/modeling/keys?tabs=data-annotations#configuring-a-primary-key
and https://learn.microsoft.com/en-us/ef/core/modeling/generated-properties?tabs=data-annotations#primary-keys
Side note: The type of the property Amount should rather be decimal than string, so this is changed in the example above. In the same spirit, an enum would maybe be a better choice for the TransactionType property.
You will need of course to modify the code using the class TransactionModel in order to take into account its new definition.
You're using #Html.XXXFor() incorrectly.
When you want to use HTML-helpers for HTML <form> input binding from a collection in your ViewModel you need to use for(), not foreach() and you need to the [int index] indexer in the For() expression.
When you need to bind a form object / form model and pass extra data to your view, use ViewData for the one-way data and Model for the two-way data.
I think that ASP.NET MVC and ASP.NET Core's view-model and form-binding system needs a re-think, as it's just plain wrong to require the ViewModel object to also be the bound form model. In my own projects I have my own extensions over ASP.NET Core to allow me to use separate types/objects cleanly.
I can't fix your ActionLink items though
<tbody>
#for( int i = 0; i < this.Model.Count; i++ ) {
<tr>
<td>
#Html.DisplayFor( m => m[i].UserName )
</td>
<td>
#Html.DisplayFor( m => m[i].Description )
</td>
<td>
#Html.DisplayFor( m => m[i].TransactionType )
</td>
<td>
#Html.DisplayFor( m => m[i].Amount )
</td>
<td>
#Html.DisplayFor( m => m[i].Date )
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
</td>
</tr>
}
</tbody>
'IEnumerable' does not contain a definition for 'ImageName' and no accessible extension method 'ImageName' accepting a first argument of type 'IEnumerable' could be found (are you missing a using directive or an assembly reference?
Is the current error I am getting, However the way im going about this seems to be wrong if anyone can help or point me in the right direction it would be appreciated. (Aslo I am using .net core 2.1 since my school computers do not support later versions :/)
Here is the view:
#model IEnumerable<Lab2Phase1.Models.Car>
#{
ViewData["Title"] = "Index";
}
<strong>Index</strong>
<p>
<a asp-action="Create">Create New</a>
</p>
<div class="col-md-8">
<form action="/Cars" method="post">
#Html.TextBox("search")
<input type="submit" />
</form>
</div>
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.Id)
</th>
<th>
#Html.DisplayNameFor(model => model.Model)
</th>
<th>
#Html.DisplayNameFor(model => model.TopSpeed)
</th>
<th>
#Html.DisplayNameFor(model => model.ImageName)
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Id)
</td>
<td>
#Html.DisplayFor(modelItem => item.Model)
</td>
<td>
#Html.DisplayFor(modelItem => item.TopSpeed)
</td>
<td>
<img src="~/Content/images/#Html.DisplayFor(modelItem => modelItem.ImageName)" style="height:200px;width:200px;"/>
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.Id }) |
#Html.ActionLink("Delete", "Delete", new { id = item.Id }, new { onclick = "return confirm('Are you sure to delete?')" })
</td>
</tr>
}
</tbody>
</table>
Here is the controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Lab2Phase1.Models;
using Microsoft.AspNetCore.Mvc;
using Lab2Phase1.CarsContext;
using System.Collections.Specialized;
using System.Drawing;
namespace Lab2Phase1.Controllers
{
public class CarsController : Controller
{
EFDataContext _dbContext = new EFDataContext();
public IActionResult Cars()
{
var data = this._dbContext.Cars.ToList();
return View(data);
}
[HttpPost]
public IActionResult Cars(string search)
{
Console.WriteLine("boot");
//search = Request.Form["search"].ToString();
var data = _dbContext.Cars.Where(c => c.Model.Contains(search));
return View(data);
}
public IActionResult Create()
{
return View();
}
[HttpPost]
public IActionResult Create([Bind("Id,Model,TopSpeed,ImageName")]Car model)
{
ModelState.Remove("Id");
model.Model = Request.Form["Model"];
model.TopSpeed = Request.Form["TopSpeed"];
model.ImageName = Request.Form["ImageName"];
if (ModelState.IsValid)
{
_dbContext.Cars.Add(model);
_dbContext.SaveChanges();
return RedirectToAction("cars");
}
return View();
}
public IActionResult Edit(int id)
{
Car data = _dbContext.Cars.Where(p => p.Id == id).FirstOrDefault();
return View("Create", data);
}
[HttpPost]
public IActionResult Edit(Car model)
{
ModelState.Remove("Id");
model.Id = Int32.Parse(Request.Form["Id"]);
model.Model = Request.Form["Model"];
model.TopSpeed = Request.Form["TopSpeed"];
model.ImageName = Request.Form["ImageName"];
if (ModelState.IsValid)
{
_dbContext.Cars.Update(model);
_dbContext.SaveChanges();
return RedirectToAction("cars");
}
return View("Create", model);
}
public IActionResult Delete(int id)
{
Car data = _dbContext.Cars.Where(p => p.Id == id).FirstOrDefault();
if (data != null)
{
_dbContext.Cars.Remove(data);
_dbContext.SaveChanges();
}
return RedirectToAction("cars");
}
}
}
Here's the model:
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Lab2Phase1.Models
{
public class Car
{
[Key]
public int Id { get; set; }
public string Model { get; set; }
public string TopSpeed { get; set; }
public string ImageName { get; set; }
}
}
<img src="~/Content/images/#Html.DisplayFor(modelItem => modelItem.ImageName)" style="height:200px;width:200px;"/>
should be
<img src="~/Content/images/#(item.ImageName)" style="height:200px;width:200px;"/>
Assuming your ImageName is a file name with extention, available in that path.
I need to display a resulting table of PO Requests which includes the PO Items (this part I figured out) and provide search functionality for all items displayed (this part I got mostly figured out).
Using the code below, which is a slightly stripped down version, I am able to display the data I need and I am able to search for nearly all I need, as long as it is data from the PORequests model (this is the "parent model"). When I try to search using the "child model," POItems, the actual objects (ID, Description, etc.) are not accessible. This was built using DatabaseFirst model and EF6.x DbContextGenerator.
//POItemData ViewModel
using FinanceSearch.Models;
namespace FinanceSearch.ViewModels
{
public class POItemData
{
public PagedList.IPagedList<PORequest> PORequests { get; set; }
public PagedList.IPagedList<POItem> POItems { get; set; }
}
}
//POItems Model
namespace FinanceSearch.Models
{
using System;
using System.Collections.Generic;
public partial class POItem
{
public int ID { get; set; }
public int Amount { get; set; }
public string Description { get; set; }
public virtual PORequest PORequest { get; set; }
}
}
//PORequest Model
namespace FinanceSearch.Models
{
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
public partial class PORequest
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public PORequest()
{
this.POAttachments = new HashSet<POAttachment>();
this.POItems = new HashSet<POItem>();
}
//other relevant stuff
public virtual ICollection<POItem> POItems { get; set; }
}
}
//Controller
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;
using FinanceSearch.Models;
using PagedList;
using System.Web.UI.WebControls;
using System.Data.Entity.SqlServer;
using FinanceSearch.ViewModels;
namespace FinanceSearch.Controllers
{
public class PORequestsController : Controller
{
private Finance db = new Finance();
public ActionResult Index(int? id, int? page, string sortOrder, string currentFilter, string poNumber,
string AppropNumber, string ContractNumber, string ItemDescription)
{
ViewBag.CurrentSort = sortOrder;
ViewBag.POSortParm = String.IsNullOrEmpty(sortOrder) ? "PONumber_desc" : "";
ViewBag.AppropNumberSortParm = sortOrder == "AppropNumber" ? "AppropNumber_desc" : "AppropNumber";
ViewBag.ContractNumberSortParm = sortOrder == "ContractNumber" ? "ContractNumber_desc" : "ContractNumber";
if (poNumber != null)
{
page = 1;
}
else
{
ponumber = currentFilter;
}
//int pageSize = 3;
int pageNumber = (page ?? 1);
var viewModel = new POItemData();
viewModel.PORequests = db.PORequests
.Include(i => i.POItems)
.OrderBy(i => i.ID).ToPagedList(pageNumber, 5);
if (!String.IsNullOrEmpty(poNumber))
{
viewModel.PORequests = viewModel.PORequests.Where(s => s.PONumber.Contains(ponumber)).ToPagedList(pageNumber, 5);
}
if (!string.IsNullOrEmpty(AppropNumber))
{
viewModel.PORequests = viewModel.PORequests.Where(x => x.AppropNumber.Contains(AppropNumber)).ToPagedList(pageNumber, 5);
}
if (!string.IsNullOrEmpty(ContractNumber))
{
viewModel.PORequests = viewModel.PORequests.Where(x => x.ContractNumber.Contains(ContractNumber)).ToPagedList(pageNumber, 5);
}
if (!string.IsNullOrEmpty(ItemDescription))
{
}
In the ItemDescription if statement, something like the following would be nice:
if (!string.IsNullOrEmpty(ItemDescription))
{
viewModel.PORequests = viewModel.PORequests.Where(x => x.POItems.Description.Contains(ItemDescription)).ToPagedList(pageNumber, 5);
}
The x.POItems.Description part doesn't exist in this sense. Apparently related to it being a list. Continuing remainder of code...
switch (sortOrder)
{
case "PONumber_desc":
viewModel.PORequests = viewModel.PORequests.OrderByDescending(s => s.PONumber).ToPagedList(pageNumber, 5);
break;
case "AppropNumber":
viewModel.PORequests = viewModel.PORequests.OrderBy(s => s.AppropNumber).ToPagedList(pageNumber, 5);
break;
case "AppropNumber_desc":
viewModel.PORequests = viewModel.PORequests.OrderByDescending(s => s.AppropNumber).ToPagedList(pageNumber, 5);
break;
case "ContractNumber":
viewModel.PORequests = viewModel.PORequests.OrderBy(s => s.ContractNumber).ToPagedList(pageNumber, 5);
break;
case "ContractNumber_desc":
viewModel.PORequests = viewModel.PORequests.OrderByDescending(s => s.ContractNumber).ToPagedList(pageNumber, 5);
break;
default:
viewModel.PORequests = viewModel.PORequests.OrderBy(s => s.PONumber).ToPagedList(pageNumber, 5);
break;
}
return View(viewModel);
}
In the code above, I included my paging and sorting stuff as well, because in the tutorials and answers I have come across, it appears to make a difference in how it all turns out, and is required for this project.
Below is the Index/View I am using to display the results:
//View (Index)
#model FinanceSearch.ViewModels.POItemData
#using PagedList.Mvc;
<link href="~/Content/PagedList.css" rel="stylesheet" type="text/css" />
#{
ViewBag.Title = "PORequests";
}
<h2>Finance</h2>
#*<p>
#Html.ActionLink("Create New", "Create")
</p>*#
<p>
<button class="btn btn-default" data-toggle="collapse" href="#MainFilter" #*data-target="#MainFilter"*# aria-multiselectable="true" aria-expanded="true">Filter By...</button>
</p>
#*<div id="accordion" role="tablist" aria-multiselectable="true">*#
#using (Html.BeginForm("Index", "PORequests", FormMethod.Get))
{
<div id="MainFilter" class="collapse">
<table class="table-responsive">
<tr>
<td>
Item:
</td>
<td>
#Html.TextBox("ItemDescription", ViewBag.CurrentFilter as string) //this is where the user would search items using a string
</td>
</tr>
<tr>
<td>
PO #:
</td>
<td>
#Html.TextBox("PONumber", ViewBag.CurrentFilter as string)
</td>
</tr>
</table>
<br />
<p>
<button class="btn btn-primary btn-sm" data-parent="#MainFilter" data-toggle="collapse" href="#SubFilterPOInfo" #*data-target="#SubFilterPOInfo"*#>Filter by PO Info...</button>
</p>
<div id="SubFilterPOInfo" class="collapse">
<table class="table-responsive">
<tr>
<td>
AppropNumber:
</td>
<td>
#Html.TextBox("AppropNumber", ViewBag.CurrentFilter as string)
</td>
</tr>
<tr>
<td>
Contract Number:
</td>
<td>
#Html.TextBox("ContractNumber", ViewBag.CurrentFilter as string)
</td>
</tr>
</table>
</div>
}
<div style="overflow-x: scroll">
<table class="table" style="white-space:nowrap">
<tr>
<th></th>
<th></th>
<th>
#Html.ActionLink("PONumber", "Index", new { sortOrder = ViewBag.POSortParm, currentFilter = ViewBag.CurrentFilter })
</th>
<th>
#Html.ActionLink("AppropNumber", "Index", new { sortOrder = ViewBag.AppropNumberSortParm, currentFilter = ViewBag.CurrentFilter })
</th>
<th>
#Html.ActionLink("ContractNumber", "Index", new { sortOrder = ViewBag.ContractNumberSortParm, currentFilter = ViewBag.CurrentFilter })
</th>
</tr>
#foreach (var item in Model.PORequests)
{
<tr>
<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>
<td>
//I am able to list the related items in the PORequests table
#foreach (var poitem in item.POItems)
{
#poitem.Description<br />
}
</td>
<td>
#Html.DisplayFor(modelItem => item.PONumber)
</td>
<td>
#Html.DisplayFor(modelItem => item.AppropNumber)
</td>
<td>
#Html.DisplayFor(modelItem => item.ContractNumber)
</td>
</tr>
}
</table>
</div>
<br />
//paging and sorting stuff
Page #(Model.PORequests.PageCount < Model.PORequests.PageNumber ? 0 : Model.PORequests.PageNumber) of #Model.PORequests.PageCount
#Html.PagedListPager(Model.PORequests, page => Url.Action("Index", new { page, sortOrder = ViewBag.CurrentSort, currentFilter = ViewBag.CurrentFilter }))
To summarize:
I don't seem to comprehend how to use the variable ItemDescription to search within the POItems list, and get the PORequest table, which includes the POItem table as a related object, to display the results. My other searches all function as expected.
I have searched, on this site in particular, but any of the similar questions involve the view end of it just displaying each item in the list, which I am able to do, or creating/editing functionality, or, perhaps, they are beyond my understanding of MVC enough where they didn't appear to be a solution.
Here are a few such examples:
MVC Code First: One-to-many relationship between own model and SimpleMembership user
EntityFramework 6. How to get related objects?
https://stackoverflow.com/questions/26372533/add-the-list-of-object-to-parent-object-using-mvc-5-and-ef-6
Any help would be greatly appreciated.
Drill down to the items in the search expression:
if (!string.IsNullOrEmpty(ItemDescription))
{
viewModel.PORequests = viewModel.PORequests.Where(x => x.POItems
.Any(i => i.Description.Contains(ItemDescription)))
.ToPagedList(pageNumber, 5);
}
I couldn't find a solution for my issue, I have tried many alternatives but I could not resolve it.
I generate my database with the model first, afterwards I have used Scaffolding to generate the Views (Index, Create, Edit, Delete..). The only view (Index) with the model use IEnumerable.
The Index View was :
#model IEnumerable<CAD_CMDBv2.Models.Location>
#{
ViewBag.Title = "Location's Management";
}
<h2>All Locations</h2>
<p>
#Html.ActionLink("Create Location", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Location.site_name)
</th>
<th>
#Html.DisplayNameFor(model => model.Location.country_name)
</th>
<th>
#Html.DisplayNameFor(model => model.Location.region_name)
</th>
<th></th>
</tr>
#foreach(var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Location.site_name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Location.country_name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Location.region_name)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.Location.location_id }) |
#Html.ActionLink("Details", "Details", new { id = item.Location.location_id }) |
#Html.ActionLink("Delete", "Delete", new { id = item.Location.location_id })
</td>
</tr>
}
</table>
I want to insert an asynchronous form for the datasearch, so that becomes:
#model IEnumerable<CAD_CMDBv2.Models.RechercheLocationViewModel>
#{
ViewBag.Title = "Location's Management";
}
<h2>All Locations</h2>
<p>
#Html.ActionLink("Create Location", "Create")
</p>
#using (Html.BeginForm("Search", "Restaurant", FormMethod.Get))
{
#Html.TextBoxFor(r => r.Recherche)
<input type="submit" value="Rechercher" />
<p>Search Results </p>
if (Model.ListeLocations.Count == 0)
{
<p> No Results but you can create it !</p>
}
else
{
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Location.site_name)
</th>
<th>
#Html.DisplayNameFor(model => model.Location.country_name)
</th>
<th>
#Html.DisplayNameFor(model => model.Location.region_name)
</th>
<th></th>
</tr>
#foreach(var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Location.site_name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Location.country_name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Location.region_name)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.Location.location_id }) |
#Html.ActionLink("Details", "Details", new { id = item.Location.location_id }) |
#Html.ActionLink("Delete", "Delete", new { id = item.Location.location_id })
</td>
</tr>
}
</table>
}
}
I have modified the model in adding a View Model class to allow in IndexView to take as model the View Model by taking over the parameters Locations and use the Search parameter:
//------------------------------------------------------------------------------
// <auto-generated>
// Ce code a été généré à partir d'un modèle.
//
// Des modifications manuelles apportées à ce fichier peuvent conduire à un comportement inattendu de votre application.
// Les modifications manuelles apportées à ce fichier sont remplacées si le code est régénéré.
// </auto-generated>
//------------------------------------------------------------------------------
namespace CAD_CMDBv2.Models
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
public partial class Location
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Location()
{
this.User = new HashSet<User>();
this.host = new HashSet<Host>();
this.client_catia = new HashSet<Client_catia>();
this.client_smartam = new HashSet<Client_smarteam>();
}
public int location_id { get; set; }
[Display(Name = "Site's Name")]
public string site_name { get; set; }
[Display(Name = "Country's Name")]
public string country_name { get; set; }
[Display(Name = "Region's Name")]
public string region_name { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<User> User { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Host> host { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Client_catia> client_catia { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Client_smarteam> client_smartam { get; set; }
}
public class RechercheLocationViewModel : IEnumerable<Location> {
public string Recherche {get; set;}
public Location Location { get; set; }
public List<Location> ListeLocations;
public IEnumerator<Location> GetEnumerator()
{
return ListeLocations.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ListeLocations.GetEnumerator();
}
}
}
The current Controller
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;
using CAD_CMDBv2.Models;
namespace CAD_CMDBv2.Areas.Locations.Controllers
{
public class LocationsController : Controller
{
private ModeleDonneesCMDBContext db = new ModeleDonneesCMDBContext();
// GET: Locations/Locations
public ActionResult Index()
{
var liste = db.Locations.ToList();
var listeTriee = liste.OrderBy(t => t.site_name);
return View(listeTriee);
}
...
But that generates two errors of the same type about IEnumerable in the Index View on the lines:
#Html.TextBoxFor(r => r.Recherche)
And
if (Model.ListeLocations.Count == 0)
I got this error:
CS1061 Error 'IEnumerable' does not contain
a definition for 'ListeLocations' and no extension method
'ListeLocations' accepting a first argument of type
'IEnumerable' could be found (are
you missing a using directive or an assembly reference ?)
What does that mean? How can I resolve this? I still have some difficulty with understanding the IEnumerable interface.
Your Location class contains the properties Recherche and ListeLocation, but an IEnumerable of that class does not have those properties.
You are using the IEnumerable of the class as an instance of that class, that can't work.
You should think about what you need your model to be, because in one part of the view you use Model as if it were a Location, and in another part (#foreach(var item in Model) {) you use it as an IEnumerable
When you use the IEnumerable interface as a model, you are telling the View you have some kind of list, collection, or something you can 'Enumerate' as a model. A list of your Location objects, so to speak, not a single one.
Edit in response to your comments:
Change the #model IEnumerable<CAD_CMDBv2.Models.RechercheLocationViewModel> to CAD_CMDBv2.Models.RechercheLocationViewModel
Then you need yo change the controller Action:
Instead of :
var liste = db.Locations.ToList();
var listeTriee = liste.OrderBy(t => t.site_name);
return View(listeTriee);
use:
var model = new RechercheLocationViewModel();
model.AddRange(db.Locations.OrderBy(t => t.site_name));
return View(model);
But that won't make it 'work':
Your search query cannot take place in the view, you will have to go back to the server for that, so the architecture of your model is not quite right; you don't need all your locations in there, an what the single Location is there for I don't understand as well. If you want to do an async search in the view, you need an AJAX call back to the server that's going to return the search result. Your form is now just going to post something back to a Search action on your controller, and I don't know what that action does.
I can only advice you to study a bit more on creating search forms with AJAX in ASP.NET MVC
This is where your error is:
var listeTriee = liste.OrderBy(t => t.site_name);
return View(listeTriee);
Instead of passing a single model to your View, you are passing a collection (IEnumerable) which indeed doesn't have the property ListeLocations.
You should create a viewmodel and put the collection in there:
public class ListeTrieeViewModel
{
...
public IEnumerable<Locations> ListeLocations {get; set;}
}
Then you can pass that model in your controller:
public ActionResult Index()
{
var liste = db.Locations.ToList();
var listeTriee = liste.OrderBy(t => t.site_name);
var viewModel = new ListeTrieeViewModel { ListeLocations = listeTriee; }
return View(viewModel);
}
Now your check in the view will work:
if (Model.ListeLocations.Count() == 0)