Son, pick the right tool for the problem and be a master of your work.
-Walter McClain
My dad’s wise words ring loud as I age, even when developing software. At Rōnin, we believe using a mature framework or component library is preferable to doing it ourselves. We go on many journeys of Digital Transformation with companies where one of our specialties is identifying existing line-of-business applications that are candidates for a complete born-in-the-cloud rewrite, allowing us to leverage all the power, features, and functions of Azure.
During these travels, we notice common development patterns. We perfect them. We teach our Rōnins and customers. One interesting pattern that has become one of our favorites is using Kendo Grid for Angular with NHibernate Linq provider and IQueryable.
Problems With Rewriting We Often Run Into
One common problem we encounter when rewriting line-of-business applications for clients is displaying tabular data to the end user. Our clients are large. Most have been in existence for over 20+ years. They are in healthcare, where regulations require them to retain data for long durations. This translates to data stores containing many, many millions of records.
The issue arises not in the data but in the development approach. Developers are lazy, and when developing UI/UX that displays data, they read it all into memory and allow a component to handle sorting, filtering, and paging on the client side. While this is fine for small datasets, performance issues occur for the user when you have record counts of over 1000.
Solution & Easy Server Side Processing
If you get this far, I assume you understand what Kendo Grid, IQueryable, and NHibernate are. Many articles and blog posts have been made about them containing deep detail, and you can just Google them.
As I mentioned above, we like mature frameworks and component libraries. Kendo, and specifically Kendo Grid for Angular, is one of our favorites to use.
On the server side, in your .NET Core API project, just grab the Telerik NuGet Kendo.DynamicLinqCore when developing your controller. This contains the IQueryable extensions that make doing server-side processing amazingly simple and elegant.
PS> Install-Package Kendo.DynamicLinqCore
Example – Web API Controller
using Kendo.DynamicLinqCore;
[HttpPost]
public IActionResult Contacts([FromBody] DataSourceRequest requestModel)
{
return _dbSession.Query<Contact>()
.Select(c => new ContactViewModel // Not required but this shows how to do projection into a view model
{
ContactId = c.ContactId,
CompanyName = c.CompanyName,
ContactName = c.ContactName,
City = c.City,
ContactTitle = c.ContactTitle
})
.ToDataSourceResult(requestModel.Take, requestModel.Skip, requestModel.Sort, requestModel.Filter, requestModel.Aggregate, requestModel.Group);
}
At first glance, you may wonder, would this not first read all the data from the database and then do the filtering, sorting, paging, etc. in memory? Yeah, that’s what I thought, too, but it doesn’t, and I have done SQL tracing to see it in action. The library is using Dynamic Linq to create Linq queries based on the request that it is given. NHibernate’s Linq provider then translates this into a proper SQL query.
Example – Angular/TypeScript
On the client-side in your Angular Component, implement the onStateChang(state: State) method and then in the Kendo Grid Component in your HTML template, set this method as the dataStateChange event handler.
Then in your method you get handed the new grid state representing the client-side version of the DataSourceRequest object. Now you will take this and pass it to the service that will then POST it up to your API controller.
public onStateChange(state: State) {
this.gridState = state;
this.contactService.load(state);
}
And your service load code will look something similar to the following.
load(newGridState?: State): Observable<GridDataResult> {
return this.http.post<GridDataResult>(`${this.baseUrl}/search`, newGridState).pipe(
catchError(this.handleError('load contacts failed’, null))
);
}
Finally, this approach works great for about 75-80% of the needs for displaying large amounts of data. It will break down when you have very complex search needs across complicated data models. In those cases, you can still use the DataSourceRequest to get all the grid state information you need. With that information, you can then construct your own NHibernate QueryOver queries.
Again, like Dad says… pick the right tool for the right problem.
Cheers!
About Rōnin Consulting – Rōnin Consulting provides software engineering and systems integration services for healthcare, financial services, distribution, technology, and other business lines. Services include custom software development and architecture, cloud and hybrid implementations, business analysis, data analysis, and project management for a range of clients from the Fortune 500 to rapidly evolving startups. For more information, please contact us today.