Large Result Set Not Loading In ASP.NET Core - asp.net-mvc

I created an out-of-the-box project (ASP.NET Core 4.6.2) using Entity Framework and added a simple data model (book). This book has a name, description, and price. I've added some sample data to my database (SQL Server 2016) to test the speed of the views. The linq query took less than a second to return 58K records. Then, the controller immediately passed the result to the view. The view spun for about 3 minutes, and then timed out and returned a 502.3 Bad Gateway.
I was under the impression that Core was extremely fast and could handle extremely large data sets (upwards of 2-6M). If you need a sample of code, here goes:
BooksController.cs:
var books = db.Books.ToList();
return View(books);
Index.cshtml:
#model IEnumerable<TestCoreApp.Models.Book>
<h2>Index</h2>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Description)
</th>
<th>
#Html.DisplayNameFor(model => model.Price)
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Description)
</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>

A system is only as strong as its code allows. Two easy/quick ways to improve response time would be:
1) Make the View asynchronous.
2) Mark the database query as "No Tracking" when no changes will be made to the data from the current View. Adding this flag to a query makes it so the context doesn't track changes to the data. So if you DID make an update, context.SaveChanges() wouldn't save them. And if your view doesn't have any edit capabilities, it should always be there.
Using my own Index view from an app I'm working on now:
public async Task<IActionResult> Index(bool hideDisabledUsers = true) {
ViewData["hideDisabledUsers"] = hideDisabledUsers;
var employeeList = _context.Employees
.OrderBy(e => e.LastName);
if (hideDisabledUsers)
employeeList = employeeList.Where(e => e.AccessLevel != AccessType.Disabled);
return View(await employeeList.AsNoTracking().ToListAsync());
}
Note the changed method signature and return type, and how the LINQ query is built in steps. The query doesn't actually hit the database until the .ToListAsync()
Someone else may be able to provide more technical answers on performance, or more ways to make it performant, but using these two methods should show a noticeable improvement to your test.

Related

Add count/sub-count to mvc view

I've got a view that is showing the breakdown of all fault categories reported and the area that they've been reported in. What I need to get is a total count of the number of faults of each category, and then a sub count of each area under that type. I've seen around that there are options to use Model.Count(), however whenever I try that it doesn't return the right number.
The view code that I'm using is below:
<table class="table table-responsive">
<tr>
#foreach (var catGroup in Model.GroupBy(item => item.job_category))
{
foreach (var itemCat in catGroup.Take(1))
{
if (itemCat.job_category != null)
{
<td>
<table id="Tbl_Total_#itemCat.job_category">
<tr>
<th>
#Html.DisplayFor(modelItem => itemCat.job_category)
</th>
</tr>
#foreach (var item in catGroup.GroupBy(item => item.job_area))
{
foreach (var itemArea in item.Take(1))
{
<tr>
<td>
#Html.DisplayFor(modelItem => itemArea.job_area)
</td>
</tr>
}
}
</table>
</td>
}
}
}
</tr>
</table>
Can someone point me in the right direction to get a count per category, and per area under the categories?
Thanks
You can get the number of element per group calling Count extension method. eg, for the category groups you can do this:
catGroup.Count()
And you can do the same for the areas:
item.Count()

MVC Entity Framework INDEX page format

I have a new MVC Entity Framework Application. When it created the Index List page - it looks like the Headers are ran together. Have small size fields and the field names are longer than the fields. How to format that page to make Header spaced out so it doesn't all run together.
Could not post a pic - something like this - sorry the data line ran together on the past.
RE NUMINT RE NAME
1 I SHAWN NEWT
#model IEnumerable<MTSapp.Models.mts_rename>
#{ ViewBag.Title = "Index"; }
<h2>Index</h2>
<p> #Html.ActionLink("Create New", "Create") </p>
<table>
<tr>
<th> #Html.DisplayNameFor(model => model.A_RENO) </th>
<th> #Html.DisplayNameFor(model => model.A_INACT) </th>
<th> #Html.DisplayNameFor(model => model.A_NAME) </th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td> #Html.DisplayFor(modelItem => item.A_RENO) </td>
<td> #Html.DisplayFor(modelItem => item.A_INACT) </td>
<td> #Html.DisplayFor(modelItem => item.A_NAME) </td>
</tr>
}
</table>
Is the page using a table or some type of grid? You may need to apply some CSS rules to get the page nicely formatted. Does the page have any CSS styling yet?

Slow view building

I am in the process of converting an old asp.net forms application to MVC. I have run into a snag where I am displaying results of a search.
I have an entity framework model of Classes, Class Details, Rosters and Class instructors.
I have a search page where I wish to display all of the class details by class selected.
View:
#using System.Diagnostics
#model IEnumerable<SafetyReports.Models.DataModel.ClassDetails>
<div>
<table>
<tr>
<th>
Class Date
</th>
<th>
Location
</th>
<th>
Region
</th>
<th>
# of Attendees
</th>
</tr>
#foreach (var c in Model)
{
Debug.WriteLine(c.ClassDetailID);
<tr>
<td>
#c.ClassDate
</td>
<td>
#c.Location
</td>
<td>
#c.Region
</td>
<td>
#c.ClassRosters.Count
</td>
<td>
<input type="button" value="Detail" onclick="alert(#c.ClassDetailID)"/>
</td>
</tr>
}
</table>
</div>
controller:
public PartialViewResult SelectCourse(string id)
{
var e = new Entities();
var i = e.ClassDetails.Where(x => x.ClassID.ToString() == id).ToList();
return PartialView("_ClassesDetail", i);
}
My problem is that it seem to take about 1 second per 2-3 classes. I have one class type that has 1300 records and it takes about 5-6 minutes to return the view. What am I doing wrong? In the asp.net forms application I have a gridview that returns the same amount of data in seconds if that long. It isnt using EF though, just a sqldatasource. Could this be lazy loading?
firstly, as seen before using a int instead of a string for 'id' will speed up your EF query.
Here's one way to speed up the view rendering (or at least make it cleaner):
In view replace all the #foreach loop by a simple :
//here Razor will automatically repeat you model displayTemplate for
//each element of your IEnumerable
#Html.DisplayForModel()
And then define a Display Template for your model
~/Views/Shared/DisplayTemplates/ClassDetails.cshtml
#model SafetyReports.Models.DataModel.ClassDetails
<tr>
<td>
#Model.ClassDate
</td>
<td>
#Model.Location
</td>
<td>
#Model.Region
</td>
<td>
#Model.ClassRosters.Count
</td>
<td>
<input type="button" value="Detail" onclick="alert(#Model.ClassDetailID)"/>
</td>
</tr>
I found my issue, I had related objects being lazy loaded, I fixed the issue with including those objects with:
var i = e.ClassDetails.Where(x => x.ClassID == id).Include(x=>x.ClassInstructors).Include(x=>x.ClassRosters).ToList();

Add table controls for asp.net mvc web application using signalR

I want to add controls to sort a table which is rendered in a partial view. The partial view is called using a signalR method. Every time the server updates the database with new information it calls the javascript method to return a new partial view to <div id="customersTable">.
I need a way to pass a query to the controller, and make the last query sent persistent so that when signalR calls the controller action again, the clients actions are remembered.
In addition to sorting I would also like to implement child rows that are expandable via a toggle button in an additional column. I'm not sure how feasible that is using partial views and signalR in this way.
I have partially solved this by adding JQuery controls to the main page. However when AJAX updates the DOM, the controls are reset to their default state, which is something I ultimately want to avoid happening.
Here is the partial view:
#model IEnumerable<XXXX.Models.CustomerModel>
<table class="table table-hover">
<tbody>
<tr>
<th class="col-sm-2">
Name
</th>
<th class="col-sm-2">
SO Number
</th>
<th class="col-sm-2">
Report Time
</th>
<th class="col-sm-2">
Report Date
</th>
<th align="center" class="col-sm-1">
System Status
</th>
</tr>
#foreach (var item in Model)
{
<tr onclick="location.href = '#(Url.Action("SystemDetails", "Monitoring", new { id = item.ID }))'" #(item.Errors == 0 ? String.Empty : "class=danger")>
<td class="col-sm-2">
#Html.DisplayFor(modelItem => item.Name)
</td>
<td class="col-sm-2">
#Html.DisplayFor(modelItem => item.SONumber)
</td>
<td class="col-sm-2">
#Html.DisplayFor(modelItem => item.ReportTime)
</td>
<td class="col-sm-2">
#Html.DisplayFor(modelItem => item.ReportDate)
</td>
<td align="center" class="col-sm-1">
#if (item.Errors == 0)
{
<i class="glyphicon glyphicon-ok"></i>
}
else if (item.Errors > 0)
{
<i class="glyphicon glyphicon-warning-sign"></i>
}
</td>
</tr>
}
</tbody>
</table>
Here is the javascript method client side:
function getAllCustomers()
{
var tbl = $('#customersTable');
$.ajax({
url: '/Monitoring/GetCustomers',
contentType: 'application/html ; charset:utf-8',
type: 'GET',
dataType: 'html'
}).success(function (result) {
tbl.empty().append(result);
}).error(function () {
});
}
And the controller method where the db context is sorted:
public ActionResult GetCustomers()
{
return PartialView("~/Views/Monitoring/_CustomersList.cshtml", db.Customers.OrderBy(c => c.SONumber).ToList());
}

MVC checkbox event in grid

I am a new bee in MVC world. I have a scenario like, i have an grid with a checkboxs as one column. When i click on checkbox an event would fire and it would update some values in database.
I am using Razor engine.
<table>
<tr>
<th>
ID
</th>
<th>
PName
</th>
<th>
PDescription
</th>
<th>
PSerialNo
</th>
<th>
PPrice
</th>
<th>
PActive
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#item.ID
</td>
<td>
#Html.ActionLink(#item.PName, "Edit", new { id = #item.ID })
</td>
<td>
#item.PDescription
</td>
<td>
#item.PSerialNo
</td>
<td>
#String.Format("{0:c}", item.PPrice)
</td>
<td>
#Html.CheckBox("chkActiveItem", item.PActive)
</td>
</tr>
}
here the content is showed in the grid. Here when i click on this check box i want to update the flag in the database.
How will i do it?
Please help.
foreach doesn't work in this case. You need to use for loop and then changes will get picked up on server side on post.
if you want to do ajax call then simply store id of each item within its row and pass that as parameter during ajax call to identify checked item on server side.

Resources