Implementing pagination in JsonResult - c#

We can implementing pagination in C#, ASP.NET in ActionResult function like this
public ActionResult Index(int? page)
{
Entities db = new Entities();
return View(db.myTable.ToList().ToPagedList(page ?? 1,8 ));
}
how to implementing pagination in JsonResult function that sending result to ajax in html view?
public JsonResult GetSearchingData(string SearchBy, string SearchValue)
{
var subCategoryToReturn = myList.Select(S => new { Name = S.Name });
return Json(subCategoryToReturn, JsonRequestBehavior.AllowGet);
}

stop thinking in terms of UI and start thinking in terms of data.
you have some data you want to paginate. That's it. Forget about MVC at this point or JSONResult or anything else that has nothing to do with the data.
One thing to be aware of, this code you posted above:
db.myTable.ToList().ToPagedList(page ??1,8 )
if you do it like this, your entire table will be returned from the database and then it will be paginated. What you want is to only return the data already paginated, so instead of returning 100 records and then only taking the first 20, only return the 20.
Don't use ToList() at that point, use something like this instead:
var myData = db.myTable.Skip(pageNumber * pageSize).Take(pageSize)
I don't have any running code to check this, but hopefully you get the idea, only return the data already paginated, so only return the data you will display and nothing more. The UI can send the page index you click on, the pageSize can be a predefined number stored in appSettings for example.

You can use, .Skip() for skipping first n elements and Take() for fetching the next n rows.
int pageNumber = 0;
int ItemsPerPage= 10;
var Results = db.myTable.ToList().Skip(ItemsPerPage* pageNumber).Take(numberOfObjectsPerPage);
Suppose that you only need 10 items Per page. Number of Pages would be equal to
TotalPages = Math.Ceiling(TotalItems/ItemsPerPage); // IF 201 Items then 201/10 ceiling = 21 pages
Now you can make Pagination buttons in html. I suggest you use Jquery Pagniation Library
https://esimakin.github.io/twbs-pagination/

Related

Very poor performance of ASP.NET MVC view

I'm having a problem with an application I made for my company. We are taking queries out of an ERP system. People can search for an article, and then the application shows them all relevant technical data, pictures and/or datasheets.
Problem is: it's loading very slow. Queries seem to run fine, but the generation of the view takes ages.
This is my search code (don't mind the Dutch parts):
public IQueryable<Item> GetItems(string vZoekString)
{
db.Configuration.LazyLoadingEnabled = false;
//Split de ZoekString
var searchTerms = vZoekString.ToLower().Split(null);
// TODO: alles in een db query
//Resultaten oplijsten
var term = searchTerms[0];
var results = db.item_general.Where(c => c.ITEM.Contains(term) || c.DSCA.Contains(term));
//Zoeken op alle zoektermen
if (searchTerms.Length > 1)
{
for (int i = 0; i < searchTerms.Length; i++)
{
var tempTerm = searchTerms[i];
results = results.Where(c => c.ITEM.Contains(tempTerm) || c.DSCA.Contains(tempTerm));
}
}
//Show
return results;
And then, these results are returned to the view like this:
public ActionResult SearchResults(string vZoekString, string filterValue, int? pageNo)
{
//Bewaking zoekstring
if (vZoekString != null)
{
pageNo = 1;
}
else
{
vZoekString = filterValue;
}
//De zoekstring doorgeven
if (vZoekString == null)
{
return RedirectToAction("Index", "Home");
}
else
{
ViewBag.ZoekString = vZoekString;
}
//Ophalen Items via Business-Object
//var vItems = new SearchItems().GetItems(vZoekString);
SearchItems vSearchItems = new SearchItems();
IQueryable<Item> vItems = vSearchItems.GetItems(vZoekString);
//Nummering
int pageSize = 10;
int page = (pageNo ?? 1);
//Show
return View(vItems.OrderBy(x => x.ITEM).AsNoTracking().ToPagedList(page, pageSize));
}
What can be wrong in my situation? Am I overlooking something?
UPDATE:
I've checked my code, and it seems that everything works very quickly, but it takes more than 10 seconds when it reaches .ToPagedList(). So my guess is that there is something wrong with that. I'm using the paged list from Nuget.
While I can't evaluate your view code without seeing it the problem could very well be in the database query.
An IQueryable does not actually load anything from the database until you use the results. So the database query will only be run after the View code has started.
Try changing the View call to:
var items = vItems.OrderBy(x => x.ITEM).AsNoTracking().ToPagedList(page, pageSize);
return View(items);
And then check to see if the View is still the bottleneck.
(This should probably be a comment instead, but I don't have the reputation....)
In most cases where you face performance issues with MVC and EF it is due to returning entities to views and getting stung by lazy loading. The reason for this is that when ASP.Net is told to send an object to the browser it needs to serialize it. The process of serialization iterates over the entity which touches lazy-load proxies, triggering those related entities to load one at a time.
You can detect this by running a profiler against your database, set a breakpoint point prior to the end of your action, then watch what queries execute as the action call returns. Lazy loading due to serialization will appear as a number of individual (TOP 1) queries being executed in rapid succession after the action has completed prior to the page rendering.
The simplest suggestion to avoid this pain is don't return entities from controllers.
IQueryable<Item> vItems = vSearchItems.GetItems(vZoekString);
var viewModels = vItems.OrderBy(x => x.ITEM)
.Select(x => new ItemViewModel
{
ItemId = x.ItemId,
// .. Continue populating view model. If model contains a hierarchy of data, map those to related view models as well.
}).ToPagedList(page, pageSize);
return View(viewModels);
The benefit of this approach:
The .Select() will result in a query that only retrieves the data you actually need to populate the view models. (The data your view needs) Faster query, less data across the wire between DB server -> App server -> Browser
This doesn't result in any lazy loads.
The caveat of this approach:
You need to take care that the .Select() expression will go to SQL, so no .Net or private functions for stuff like translating/formatting data. Populate raw values into the view model then expose properties on the view model to do the translation which will serialize that formatted data to the client. Basic stuff like FullName = x.FirstName + " " + x.LastName is fine, but avoid things like OrderDate = DateTime.Parse(x.DateAsISO) if the DB stored dates as strings for example.
You can leverage mappers like Automapper to assist with mapping between Entity and ViewModel. Provided the mapping tool inspects/traverses the destination to populate rather than the source, you should be good. Automapper does support integration within IQueryable so it would be a worthwhile investment to research that if you want to leverage a mapper.

Server-Side Paging for improve performance

I followed the instruction of Pagination for Angular step by step in it's documentation that is here https://www.npmjs.com/package/ngx-pagination.
My pagination works perfect and I don't have any problem with that. However since I'm working with a large data-set - I don't want to work with the full collection in memory, and need some kind of server-side paging, where the server sends just a single page at a time. As mentioned in that article I should use totalItems parameter and use count, but I don't know how? How should I set the total?
<table class='table' *ngIf="collection">
<tbody>
<tr *ngFor="let item of collection |
paginate: { itemsPerPage: 10, currentPage: p, totalItems: total }">
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
</tr>
</tbody>
And my WEB API is like this:
[HttpGet("[action]")]
public async Task<IEnumerable<MyClass>> MyMethod()
{
int perPage = 10;
int start = (page - 1) * perPage;
int end = start + perPage;
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("externalAPI");
MediaTypeWithQualityHeaderValue contentType =
new MediaTypeWithQualityHeaderValue("application/json");
client.DefaultRequestHeaders.Accept.Add(contentType);
HttpResponseMessage response = await client.GetAsync(client.BaseAddress);
string content = await response.Content.ReadAsStringAsync();
List<MyClass> data = JsonConvert.DeserializeObject<List<MyClass>>(content);
return data.Skip(start).Take(perPage).ToList();
}
}
And:
p: number = 1;
total: number;
http.get('url', {
params: {
page : this.p
}
}).subscribe(result => {
this.collections = result.json() as Collection[];
}, error => console.error(error));
in order to paginate on the server side you need to have two things:
pageSize or itemsPerPage in your case
pageNumber which is basically your currentPage
You need to send these two values to your webapi so it knows what data to return.
They would become parameters to your action and you can then pass them through to webapi.
how you paginate in webapi, it depends on your code. If you use EntityFramework it's straight forward with Take and Skip methods. If you have a stored procedure ( so T-SQL ) you can do it with Fetch and Offset.
A word of caution on pageNumber. Page 1 on your UI needs to be become page 0 on the server side, so when your UI requests page 1 that is basically page 0 of the data. Page 2 UI side, becomes Page 1 data side, so you probably pass
pageNumber - 1 to the back end. Just keep this in mind.
totalItems is a number that comes from the back end.
Let's say your web api returns paginated data which looks like this:
public class myReturnedData
{
public string someData1 { get;set; }
public string someData2 { get;set }
}
your api returns a list of this class basically.
At this point create another object which looks like this:
public class myPaginatedReturnedData
{
public List<myReturnedData> { get; set; }
public int TotalItemsCount { get; set; }
}
your front end has no way of knowing what the total count is since it only receives one page of data so you need to get that number back from the API and this is one way of doing it.
So before you paginate, on the server side, you do a total count of your items and then you paginate the data and finally send back both these items.
On the front end side, you will have pageSize, and totalItemsCount and you can use this to calculate how many page indexes you should display to the user.
If your pageSize is 10 and totalItemsCount is 55 then your page index will be from 1 to 6, with page 6 only showing 5 items. You can easily write a method, on the client side for this calculation.
<-- extra details -->
change this:
public async Task<IEnumerable<MyClass>> MyMethod()
to
public async Task<myPaginatedReturnedData> MyMethod()
I've basically changed your original returned class to the new one in my example which is a wrapper of yours plus the totalCount value.
This allows you to set the value in your front end since you are now returning it together with your actual paginated data.
On the client side, the response of the API will be a string.
You could parse the response string into a JSON object, using something like
var apiData = JSON.parse(responseString)
This gives you an object and you can access your data from there.

Selected column in For Loop ASP.NET MVC3

I have a following function in a controller. It is called from index.cshtml. var relays works fine and I got the replays.count(). PRESETDETAILS has some columns from which I want to access specific columns in a loop (like I have mentioned in response.write).
Kindly tell me how I can get the specific columns in the for loop.
#region "Apply Preset Handlers"
public ActionResult btnApply_Click(int? id)
{
var relays = db.PRESETDETAILS.ToList().Where(t => (t.PRESETID).Equals(id));
for (int k = 0; k < relays.Count(); k++)
{
Response.Write(relays.Select(s => new relays{PRESETDETAILID = s.PRESETDETAILID }).ToString());
}
return View("Index");
}
#endregion
you need to loop through them, you're simply select the same things over and over...
var relays = db.PRESETDETAILS.Where(t => t.PRESETID == id).ToList();
foreach (var replay in replays)
{
Response.Write(string.Format("{0}", relay.ID));
}
NOW... looking at your code:
always use ToList() at the end of the query;
ToList() actually makes the call to the database, until then, it's just a promise
Don't use Response.Write in your Controller, send the data to the View instead
your code should be:
public ActionResult btnApply_Click(int? id)
{
var model = db.PRESETDETAILS.Where(t => t.PRESETID == id).ToList();
return View(model);
}
in your View you can loop through the records
#model List<YOUR_CLASS>
<ul>
#foreach(var replay in Model)
{
<li>#replay.ID</li>
}
</ul>
I can see that some MVC conventions are not yet in place with you, and I know it can be somewhat overwhelm from someone that comes from WebForms or a linear code approach, but why don't you take some time and check the FREE available course about ASP.MVC in the ASP.NET Website?
See the videos in the right side of this page: http://www.asp.net/mvc

Caching/compiling complex Linq query (Entity Framework)

I have a complex Entity Framework query. My performance bottleneck is not actually querying the database, but translating the IQueryable into query text.
My code is something like this:
var query = context.Hands.Where(...)
if(x)
query = query.where(...)
....
var result = query.OrderBy(...)
var page = result.skip(500 * pageNumber).Take(500).ToList(); //loong time here, even before calling the DB
do
{
foreach(var h in page) { ... }
pageNumber += 1;
page = result.skip(500 * pageNumber).Take(500).ToList(); //same here
}
while(y)
What can I do? I am using DbContext (with SQLite), so I can't use precompiled query (and even then, it would be cumbersome with query building algorithm like this).
What I basically need, is to cache a "page" query and only change the "skip" and "take" parameters, without recompiling it from the ground up each time.
Your premise is incorrect. Because you have a ToList call at the end of your query you are querying the database where you've indicated, to construct the list. You're not deferring execution any longer. That's why it takes so long. You aren't spending a long time constructing the query, it's taking a long time to go to the database and actually execute it.
If it helps you can use the following method to do the pagination for you. It will defer fetching each page until you ask for the next one:
public static IEnumerable<IEnumerable<T>> Paginate<T>(
this IQueryable<T> query, int pagesize)
{
int pageNumber = 0;
var page = query.Take(pagesize).ToList();
while (page.Any())
{
yield return page;
pageNumber++;
page = query.Skip(pageNumber * pagesize)
.Take(pagesize)
.ToList();
}
}
So if you had this code:
var result = query.OrderBy(...);
var pages = result.Paginate();//still haven't hit the database
//each iteration of this loop will go query the DB once to get that page
foreach(var page in pages)
{
//use page
}
If you want to get an IEnumerable<IQueryable<T>> in which you have all of the pages as queries (meaning you could add further filters to them before sending them to the database) then the major problem you have is that you don't know how many pages there will be. You need to actually execute a given query to know if it's the last page or not. You either need to fetch each page as you go, as this code does, or you need to query the count of the un-paged query at the start (which means one more DB query than you would otherwise need). Doing that would look like:
public static IEnumerable<IQueryable<T>> Paginate<T>(
this IQueryable<T> query, int pagesize)
{
//note that this is hitting the DB
int numPages = (int)Math.Ceiling(query.Count() / (double)pagesize);
for (int i = 0; i < numPages; i++)
{
var page = query.Skip(i * pagesize)
.Take(pagesize);
yield return page;
}
}

How should I implement sorting with Linq to Sql and ASP.NET MVC?

I have an ASP.NET MVC application where I'm displaying a list of Products.
// Product Controller
public ActionResult List(string sort, int page)
{
var products = _productService.GetProducts(page, sort);
return View(products);
}
// ProductService.cs
public IEnumerable<Products> GetProducts(int pageIndex, string sort)
{
return _productRepository.GetProducts(pageIndex, 50, sort);
}
// ProductsRepository.cs
public IEnumerable<Products> GetProducts(int pageIndex, int pageSize, string sort)
{
using(var db = new ShopDataContext())
{
return db.Products.OrderBy(??).Skip(pageIndex * pageSize).Take(pageSize).ToList();
}
}
I have a very simple service layer and repository.
How can I sort my Linq to SQL query by some arbitrary sort string/expression that I pull from the query string of my action?
/products?sort=hot&page=2
You can use the Dynamic Linq for doing this and then you can simply make something like OrderBy("Hot ASC"). Check below link:
Dynamic LINQ
this page would help better
http://www.asp.net/entity-framework/tutorials/sorting-filtering-and-paging-with-the-entity-framework-in-an-asp-net-mvc-application
also you can use pagedlist.mvc for your paginated list
https://github.com/TroyGoode/PagedList
it is so simple and if you install the mvc version you just need to write on view this code then all pages and next buttons created automatically
#Html.PagedListPager((IPagedList)ViewBag.OnePageOfProducts, page => Url.Action("Index", new { page = page }))
if you get stack on any step please ask

Categories