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?
Any help would be greatly appreciated.