Binding Kendo Grid in .NET Core with Example

Short answer: To bind a Kendo Grid in ASP.NET Core, use the Telerik UI for ASP.NET Core Html.Kendo().Grid<T>() helper with an Ajax Read action. The action accepts a DataSourceRequest and returns a DataSourceResult, which holds the current page of rows and the total row count.

This tutorial covers Kendo Grid data binding in ASP.NET Core in one complete project. You'll create the app, set up a database with sample rows, and bind the grid four ways: local binding, Ajax binding with server-side paging, sorting and filtering, Ajax binding with client-side operations, and custom (OData) binding. Then you'll add create, update and delete, and finish with performance, security and troubleshooting advice. Each step ends with what you should see, so you can tell right away if something is off.

Choose the matching setup: the Html.Kendo() examples use Telerik UI for ASP.NET Core, a commercial package (a free trial is available), and not only the Kendo UI for jQuery scripts. Both trial and commercial installs need a Telerik license key file, otherwise you'll see build warnings and run-time watermarks. Follow Telerik's NuGet setup, the license key guide and the Ajax binding guide for the exact package, scripts and styles that match your release.

Binding Kendo Grid in .NET Core with Example

1. What the Kendo Grid does in ASP.NET Core

The Kendo Grid shows rows of data in a table and supports paging, sorting, filtering, grouping and editing. In ASP.NET Core you declare it in a Razor view with a C# helper. The helper renders the HTML and the JavaScript that powers the grid in the browser.

Binding means telling the grid where its rows come from. Pick the mode that fits your data size:

  • Local binding: the rows are rendered with the page. Best for small, fixed lists.
  • Ajax binding with server operations: the grid asks your controller for one page at a time. Best for large tables.
  • Ajax binding with client operations: the grid loads all rows once, then pages and sorts in the browser. Best for a few hundred rows.
  • Custom binding: the grid reads from a service with its own protocol, such as OData.

2. Prerequisites

  • The .NET SDK (check Telerik's system requirements for the .NET versions your release supports)
  • SQL Server or SQL Server LocalDB, or another database if you swap the EF Core provider
  • A Telerik UI for ASP.NET Core license or free trial, plus the license key file from your Telerik account
  • A project with nullable reference types and implicit usings enabled, which is the default in current MVC templates
  • Basic C#, MVC and Razor knowledge

3. Create the project and install packages

  1. Create an MVC project.
    .NET CLI
    dotnet new mvc -n CodingvilaGridDemo
    cd CodingvilaGridDemo
  2. Install Entity Framework Core. The last command installs the EF Core command-line tool, or updates it if you already have it.
    .NET CLI
    dotnet add package Microsoft.EntityFrameworkCore.SqlServer
    dotnet add package Microsoft.EntityFrameworkCore.Design
    dotnet tool update --global dotnet-ef
  3. Install the Telerik wrapper. Telerik.UI.for.AspNet.Core is served from Telerik's NuGet feed, so add that feed first as described in the NuGet setup guide. Then install the package:
    .NET CLI
    dotnet add package Telerik.UI.for.AspNet.Core
  4. Activate your license. The package depends on Telerik.Licensing, which checks your license key during build. Download telerik-license.txt from your Telerik account and save it where Telerik's license guide says, or in your project root.

Expected result: dotnet build succeeds without license errors. If the Telerik package can't be found, the feed or credentials aren't configured yet. If the build reports a license problem, the key file is missing or belongs to a different product.

By the end, your project will contain these files:

Project layout
CodingvilaGridDemo/
  Controllers/HomeController.cs
  Data/AppDbContext.cs
  Models/CodingvilaModel.cs
  Models/CodingvilaViewModel.cs
  Views/Home/Index.cshtml
  Views/Home/Local.cshtml
  Views/Home/ClientOps.cshtml
  Views/Home/Custom.cshtml
  Views/Home/Editable.cshtml
  Views/Shared/_Layout.cshtml
  Views/_ViewImports.cshtml
  appsettings.json
  Program.cs

4. Create the models, database context and sample data

You'll use two classes. The entity maps to the database table. The view model is what the grid sees. Keeping them separate means you never send columns to the browser that the grid doesn't need.

  1. Entity (Models/CodingvilaModel.cs):
    C#
    using System.ComponentModel.DataAnnotations;
    
    namespace CodingvilaGridDemo.Models;
    
    public class CodingvilaModel
    {
        public int Id { get; set; }
    
        [Required, MaxLength(100)]
        public string Name { get; set; } = "";
    
        [MaxLength(250)]
        public string Description { get; set; } = "";
    }
  2. View model (Models/CodingvilaViewModel.cs). The validation attributes also drive the grid's editing validation in section 9. Description is declared string? on purpose. With nullable reference types enabled, MVC treats a non-nullable string as required, so an empty description would fail validation when you save a row.
    C#
    using System.ComponentModel.DataAnnotations;
    
    namespace CodingvilaGridDemo.Models;
    
    public class CodingvilaViewModel
    {
        public int Id { get; set; }
    
        [Required, StringLength(100)]
        public string Name { get; set; } = "";
    
        [StringLength(250)]
        public string? Description { get; set; }
    }
  3. Database context with 25 seed rows (Data/AppDbContext.cs). Seeding gives you enough rows to see paging work.
    C#
    using CodingvilaGridDemo.Models;
    using Microsoft.EntityFrameworkCore;
    
    namespace CodingvilaGridDemo.Data;
    
    public class AppDbContext : DbContext
    {
        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
    
        public DbSet<CodingvilaModel> CodingvilaModels => Set<CodingvilaModel>();
    
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<CodingvilaModel>().HasData(
                Enumerable.Range(1, 25).Select(i => new CodingvilaModel
                {
                    Id = i,
                    Name = $"Developer {i}",
                    Description = $"Sample role {i}"
                }));
        }
    }
  4. Connection string (appsettings.json). This example uses SQL Server LocalDB on Windows. Replace it with your own server if needed, and keep your existing Logging and AllowedHosts entries. Don't commit real production credentials.
    JSON
    {
      "ConnectionStrings": {
        "DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=CodingvilaGridDemo;Trusted_Connection=True;TrustServerCertificate=True"
      }
    }
  5. Create the database.
    .NET CLI
    dotnet ef migrations add InitialCreate
    dotnet ef database update

Expected result: a CodingvilaGridDemo database with one table of 25 rows.

5. Configure Kendo in the app

  1. Program.cs. Two settings matter here. AddKendo() registers the Telerik services. PropertyNamingPolicy = null keeps JSON property names in PascalCase, because ASP.NET Core returns camelCase by default and the data-bound Telerik components expect PascalCase. Without it the grid shows empty or wrong cells.
    C#
    using CodingvilaGridDemo.Data;
    using Kendo.Mvc.Extensions;
    using Microsoft.EntityFrameworkCore;
    
    var builder = WebApplication.CreateBuilder(args);
    
    builder.Services.AddControllersWithViews()
        .AddJsonOptions(options =>
            options.JsonSerializerOptions.PropertyNamingPolicy = null);
    
    builder.Services.AddKendo();
    
    builder.Services.AddDbContext<AppDbContext>(options =>
        options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
    
    var app = builder.Build();
    
    if (!app.Environment.IsDevelopment())
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }
    
    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseRouting();
    app.UseAuthorization();
    
    app.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
    
    app.Run();
  2. _ViewImports.cshtml. Add the Kendo imports below the existing lines:
    Razor
    @using CodingvilaGridDemo
    @using CodingvilaGridDemo.Models
    @using Kendo.Mvc.UI
    @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
    @addTagHelper *, Kendo.Mvc
  3. _Layout.cshtml. Add the styles and scripts inside <head>, and keep @RenderBody() in the body. The pattern looks like this, where {VERSION} and {THEME} are your Telerik release and theme file. File names and paths differ between releases, so copy the real values from the installation guide. Load order matters: jQuery first, then Kendo, then the ASP.NET Core integration script.
    HTML
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>@ViewData["Title"] - Codingvila Grid Demo</title>
        <link href="https://kendo.cdn.telerik.com/{VERSION}/styles/{THEME}.css" rel="stylesheet" />
        <script src="https://kendo.cdn.telerik.com/{VERSION}/js/jquery.min.js"></script>
        <script src="https://kendo.cdn.telerik.com/{VERSION}/js/kendo.all.min.js"></script>
        <script src="https://kendo.cdn.telerik.com/{VERSION}/js/kendo.aspnetmvc.min.js"></script>
    </head>

Expected result: the app still builds and runs. You won't see a grid yet.

6. How the grid and your controller talk to each other

Two Telerik types do most of the work. DataSourceRequest describes what the grid wants: the page number, page size, sort order and filters. DataSourceResult is what you send back: the rows for that page and the total count, which the pager needs. The ToDataSourceResultAsync extension method connects the two by applying the request to your IQueryable. Read the diagram below from top to bottom.

Kendo Grid Ajax binding request flow The grid in the browser sends a DataSourceRequest to the Read action. The action queries the database through EF Core and returns JSON with Data and Total back to the grid. Grid in the browser pages, sorts, filters Read action returns a DataSourceResult Database queried through EF Core DataSourceRequest IQueryable query JSON: Data + Total
The grid sends its paging, sorting and filtering state to the action. The action runs one database query and replies with the rows for that page and the total count. The dashed arrow is the JSON response.

7. Kendo Grid data binding techniques

All four techniques share one controller. Create or replace Controllers/HomeController.cs with the code below. The create, update and delete actions come later in section 9.

C#
using CodingvilaGridDemo.Data;
using CodingvilaGridDemo.Models;
using Kendo.Mvc.Extensions;
using Kendo.Mvc.UI;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace CodingvilaGridDemo.Controllers;

public class HomeController : Controller
{
    private readonly AppDbContext _context;

    public HomeController(AppDbContext context) => _context = context;

    // 7.2 Ajax binding with server operations
    public IActionResult Index() => View();

    // 7.1 Local binding
    public async Task<IActionResult> Local()
    {
        var rows = await BaseQuery().ToListAsync();
        return View(rows);
    }

    // 7.4 Client operations
    public IActionResult ClientOps() => View();

    // 7.5 Custom (OData) binding
    public IActionResult Custom() => View();

    // 9. Editing
    public IActionResult Editable() => View();

    // Shared Read action used by Index, ClientOps and Editable
    [HttpPost]
    public async Task<IActionResult> Read([DataSourceRequest] DataSourceRequest request)
    {
        var result = await BaseQuery().ToDataSourceResultAsync(request);
        return Json(result);
    }

    // Projects the entity to the view model so only needed columns are queried
    private IQueryable<CodingvilaViewModel> BaseQuery() =>
        _context.CodingvilaModels
            .AsNoTracking()
            .Select(m => new CodingvilaViewModel
            {
                Id = m.Id,
                Name = m.Name,
                Description = m.Description
            });
}

7.1 Local data binding

Local binding passes the rows to the view as the model. Use it for small lists that don't change while the page is open. There is no Read request. Create Views/Home/Local.cshtml:

Razor
@model IEnumerable<CodingvilaViewModel>
@{
    ViewData["Title"] = "Local binding";
}

<h2>Local binding</h2>

@(Html.Kendo().Grid(Model)
    .Name("LocalGrid")
    .Columns(columns =>
    {
        columns.Bound(c => c.Id).Title("ID");
        columns.Bound(c => c.Name).Title("Name");
        columns.Bound(c => c.Description).Title("Description");
    })
)

Expected result at /Home/Local: a table with all 25 rows. For paging, sorting and filtering in the browser, use section 7.4.

7.2 Ajax binding with server-side paging, sorting and filtering

This is the setup most real projects need. The grid sends a request every time the user changes the page, sort order or filter, and the Read action from the controller answers it. The Ajax Read uses POST by default, which is why the action is marked [HttpPost]. Create Views/Home/Index.cshtml:

Razor
@{
    ViewData["Title"] = "Ajax binding";
}

<h2>Ajax binding with server operations</h2>

@(Html.Kendo().Grid<CodingvilaViewModel>()
    .Name("CodingvilaGrid")
    .Columns(columns =>
    {
        columns.Bound(c => c.Id).Title("ID");
        columns.Bound(c => c.Name).Title("Name");
        columns.Bound(c => c.Description).Title("Description");
    })
    .Pageable()
    .Sortable()
    .Filterable()
    .DataSource(dataSource => dataSource
        .Ajax()
        .PageSize(10)
        .Sort(sort => sort.Add(m => m.Id).Ascending())
        .Read(read => read.Action("Read", "Home"))
    )
    .HtmlAttributes(new { style = "height: 400px;" })
)

Expected result at /: 10 rows per page, a pager showing 25 items in total, and sortable and filterable columns. Every page click, sort or filter sends a new request, which you can confirm in the browser's network tab. The Sort call gives the first load a stable default order, which paging needs.

7.3 What the Read action actually returns

Because EF Core translates the query, the database does the paging, sorting and filtering, and only one page of rows travels over the network. The result serializes to JSON like this (shortened):

JSON
{
  "Data": [ { "Id": 1, "Name": "Developer 1", "Description": "Sample role 1" } ],
  "Total": 25,
  "AggregateResults": null,
  "Errors": null
}

The grid uses Total to draw the pager. Don't return a plain list from an action bound this way, or paging breaks.

7.4 Client-side operations (paging and sorting in the browser)

If the table is small, load all rows once and let the browser handle paging, sorting and filtering. Set ServerOperation(false) on the data source. The same Read action works, because the grid doesn't ask for a page, so the action returns every row. Create Views/Home/ClientOps.cshtml:

Razor
@{
    ViewData["Title"] = "Client operations";
}

<h2>Ajax binding with client operations</h2>

@(Html.Kendo().Grid<CodingvilaViewModel>()
    .Name("ClientOpsGrid")
    .Columns(columns =>
    {
        columns.Bound(c => c.Id).Title("ID");
        columns.Bound(c => c.Name).Title("Name");
        columns.Bound(c => c.Description).Title("Description");
    })
    .Pageable()
    .Sortable()
    .Filterable()
    .DataSource(dataSource => dataSource
        .Ajax()
        .PageSize(10)
        .ServerOperation(false)
        .Read(read => read.Action("Read", "Home"))
    )
    .HtmlAttributes(new { style = "height: 400px;" })
)

Expected result at /Home/ClientOps: the grid looks the same as in 7.2, but after the first load, paging, sorting and filtering send no new requests. Check the network tab to confirm.

Limit: every row is downloaded and held in browser memory, so this suits a few hundred rows, not tens of thousands. For larger tables, use server operations from section 7.2.

7.5 Custom data binding (OData v4)

Use custom binding when the data comes from a service that doesn't follow the DataSourceRequest pattern, such as an OData v4 endpoint. You describe the service type and URL, and the grid builds its requests in that service's format. This example reads from Telerik's public OData v4 demo service. It's meant for demos and can change, so point it at your own service in a real project. Create Views/Home/Custom.cshtml:

Razor
@{
    ViewData["Title"] = "Custom binding";
}

<h2>Custom binding (OData v4)</h2>

@(Html.Kendo().Grid<dynamic>()
    .Name("OrdersGrid")
    .Columns(columns =>
    {
        columns.Bound("OrderID").Title("Order ID");
        columns.Bound("ShipName").Title("Ship name");
        columns.Bound("ShipCity").Title("Ship city");
    })
    .Pageable()
    .Sortable()
    .DataSource(dataSource => dataSource
        .Custom()
        .Type("odata-v4")
        .PageSize(10)
        .ServerPaging(true)
        .ServerSorting(true)
        .Transport(transport => transport
            .Read(read => read.Url("https://demos.telerik.com/service/v2/odata/Orders"))
        )
    )
    .HtmlAttributes(new { style = "height: 400px;" })
)

Expected result at /Home/Custom: a paged, sortable grid of sample orders. No controller action is involved, because the browser talks to the OData service directly. That also means the service must allow cross-origin requests (CORS) from your site. If the grid stays empty, open the browser console. A CORS error means the service rejects your origin, so switch to your own endpoint. Telerik's OData-v4 binding guide shows how to build one with ASP.NET Core.

8. Which binding mode should you choose?

Answer the two questions in the flowchart below, starting at the top.

How to choose a Kendo Grid binding mode If the data does not come from your own database, use custom binding. If it does and there are more than a few hundred rows, use Ajax with server operations. Otherwise use Ajax with client operations, or local binding for a static list. Pick a binding mode Data from your own database? No Custom binding (OData, services) Yes More than a few hundred rows? Yes Ajax + server operations No Ajax + client operations or local for static lists
Start at the top. Custom binding is for data that lives outside your database, and the row count decides between server and client operations.
Mode Where operations run Best for
Local (Grid(Model)) Rows rendered with the page Small, fixed lists
Ajax, server operations Database, through your action Large tables
Ajax, client operations Browser A few hundred rows
Custom (OData) The external service Services with their own protocol

9. Add create, update and delete

Once the grid reads data correctly, you can let users edit it. Each operation is a separate POST action. Each one returns the affected row through ToDataSourceResultAsync(request, ModelState), so validation errors reach the grid and appear next to the fields.

  1. Add the actions to HomeController:
    C#
    [HttpPost]
    public async Task<IActionResult> Create([DataSourceRequest] DataSourceRequest request, CodingvilaViewModel model)
    {
        if (ModelState.IsValid)
        {
            var entity = new CodingvilaModel { Name = model.Name, Description = model.Description ?? "" };
            _context.CodingvilaModels.Add(entity);
            await _context.SaveChangesAsync();
            model.Id = entity.Id;
        }
    
        var result = await new[] { model }.ToDataSourceResultAsync(request, ModelState);
        return Json(result);
    }
    
    [HttpPost]
    public async Task<IActionResult> Update([DataSourceRequest] DataSourceRequest request, CodingvilaViewModel model)
    {
        if (ModelState.IsValid)
        {
            var entity = await _context.CodingvilaModels.FindAsync(model.Id);
            if (entity == null)
            {
                ModelState.AddModelError("", "This row no longer exists.");
            }
            else
            {
                entity.Name = model.Name;
                entity.Description = model.Description ?? "";
                await _context.SaveChangesAsync();
            }
        }
    
        var result = await new[] { model }.ToDataSourceResultAsync(request, ModelState);
        return Json(result);
    }
    
    [HttpPost]
    public async Task<IActionResult> Destroy([DataSourceRequest] DataSourceRequest request, CodingvilaViewModel model)
    {
        var entity = await _context.CodingvilaModels.FindAsync(model.Id);
        if (entity != null)
        {
            _context.CodingvilaModels.Remove(entity);
            await _context.SaveChangesAsync();
        }
    
        var result = await new[] { model }.ToDataSourceResultAsync(request, ModelState);
        return Json(result);
    }
  2. Create the view Views/Home/Editable.cshtml. The Model call tells the grid which field is the row key, and the ID is read-only because the database generates it.
    Razor
    @{
        ViewData["Title"] = "Editable grid";
    }
    
    <h2>Editable grid</h2>
    
    @(Html.Kendo().Grid<CodingvilaViewModel>()
        .Name("EditableGrid")
        .Columns(columns =>
        {
            columns.Bound(c => c.Id).Title("ID").Width(80);
            columns.Bound(c => c.Name).Title("Name");
            columns.Bound(c => c.Description).Title("Description");
            columns.Command(command =>
            {
                command.Edit();
                command.Destroy();
            }).Width(220);
        })
        .ToolBar(toolbar => toolbar.Create())
        .Editable(editable => editable.Mode(GridEditMode.InLine))
        .Pageable()
        .Sortable()
        .DataSource(dataSource => dataSource
            .Ajax()
            .PageSize(10)
            .Sort(sort => sort.Add(m => m.Id).Ascending())
            .Model(model =>
            {
                model.Id(m => m.Id);
                model.Field(m => m.Id).Editable(false);
            })
            .Read(read => read.Action("Read", "Home"))
            .Create(create => create.Action("Create", "Home"))
            .Update(update => update.Action("Update", "Home"))
            .Destroy(destroy => destroy.Action("Destroy", "Home"))
        )
        .HtmlAttributes(new { style = "height: 460px;" })
    )

Expected result at /Home/Editable: an "Add new record" button above the grid, and Edit and Delete buttons on each row. Add a row with an empty name and the grid shows a validation message instead of saving it. Add a valid row, with or without a description, and it appears with a database-generated ID. Refresh the page to confirm it persisted.

Beyond editing, a few one-line additions are worth trying. .Groupable() lets users drag a column header into a group bar, and .Scrollable() keeps a fixed-height grid scrollable. With server operations, ToDataSourceResultAsync processes grouping as well.

10. Using the tag helper instead of the HTML helper

Telerik also ships tag helpers, which look like HTML elements. They configure the same component. Use whichever style your team prefers, and don't mix both styles for one grid. This tag helper version binds to the same Read action (the addTagHelper line in _ViewImports.cshtml from section 5 enables it):

Razor
<kendo-grid name="TagHelperGrid" height="400">
    <datasource type="DataSourceTagHelperType.Ajax" page-size="10" server-operation="true">
        <schema data="Data" total="Total">
            <model id="Id"></model>
        </schema>
        <transport>
            <read url="@Url.Action("Read", "Home")" type="POST"/>
        </transport>
    </datasource>
    <columns>
        <column field="Id" title="ID"></column>
        <column field="Name" title="Name"></column>
        <column field="Description" title="Description"></column>
    </columns>
    <pageable enabled="true"/>
    <sortable enabled="true"/>
    <filterable enabled="true"/>
</kendo-grid>

The schema element tells the grid to read rows from Data and the count from Total. This snippet covers reading only. Attribute names can vary slightly between Telerik releases, so check them against the documentation for your version. For Razor Pages, the approach is the same, with handler methods such as OnPostRead that return ToDataSourceResult(request) in a JsonResult.

11. Performance and security best practices

  • Keep the work in the database. Pass an IQueryable to ToDataSourceResultAsync. If you call ToList() first, every row loads into memory and paging happens in your app.
  • Project to a view model. The Select in BaseQuery fetches only the columns the grid shows.
  • Use AsNoTracking() on read-only queries, since the grid's rows don't need change tracking.
  • Index sorted and filtered columns. If users sort by Name often, an index on that column helps. Check real query plans on your data.
  • Set a sensible page size. Ten to fifty rows per page is typical. Avoid loading thousands of rows into one page.
  • Protect your actions. Add [Authorize] to private data, and check that the signed-in user may read or change each row.
  • Validate on the server. The grid's client-side validation is a convenience. The ModelState.IsValid checks are what protect your data.
  • Handle antiforgery tokens. If your app validates antiforgery tokens on POST requests, send the token with every grid read and write request. Telerik documents how to do this for your release.
  • Keep your license key out of source control. In CI/CD, supply it through Telerik's documented environment variables instead of committing it.
  • Use HTTPS for all pages and for any custom binding endpoint.

12. Run and test the project

  1. Run dotnet run and open the HTTPS URL it prints.
  2. Open / and confirm 10 rows and a pager that shows 25 items.
  3. Click the Name header twice and confirm the order flips between ascending and descending.
  4. Use the filter on the Name column and confirm the total updates.
  5. Open the browser's network tab and confirm each page, sort or filter sends a POST to /Home/Read.
  6. Open /Home/ClientOps and confirm paging sends no new requests after the first load.
  7. Open /Home/Editable, add, edit and delete a row, and confirm the changes survive a page refresh.

13. Common errors and how to fix them

  • The grid renders but cells are empty or show undefined: the JSON is camelCase. Set PropertyNamingPolicy = null as shown in section 5.
  • The request fails with 404, 405 or 400: the action's HTTP verb doesn't match the grid's read request. The default is POST, so use [HttpPost], or change the grid's read request type. Also check for antiforgery validation.
  • The pager shows no total or paging doesn't work: the action returned a plain list. Return ToDataSourceResultAsync(request) instead.
  • Kendo is not defined or the grid doesn't appear: a script is missing or loaded in the wrong order. Load jQuery, then kendo.all.min.js, then kendo.aspnetmvc.min.js.
  • The helper isn't recognized in the view: check the Kendo imports in _ViewImports.cshtml and confirm the NuGet package restored.
  • Build errors or warnings about a Telerik license, or a watermark or banner on the page: the license key file is missing, expired or belongs to a different product. Download a fresh telerik-license.txt from your Telerik account and rebuild.
  • Saving a row fails with "The Description field is required": Description is a non-nullable string in the view model. Declare it as string? as shown in section 4.
  • Edits don't save, or new rows lose their ID: make sure the data source declares Model with an Id, and that Create returns the model with the database-generated ID.
  • Database errors on first run: confirm the connection string and that dotnet ef database update completed.

14. Frequently asked questions

What is the difference between server and client operations? With server operations, each page, sort or filter is a new request and the database does the work. With client operations, all rows load once and the browser does the work.

Can I use the grid without the Telerik NuGet package? The Html.Kendo() helpers need the Telerik UI for ASP.NET Core package. Without it, you'd use the Kendo UI for jQuery grid and write the JavaScript yourself.

Do I need a license key to try it? Yes. Telerik UI for ASP.NET Core needs a license key file for both trial and commercial licenses. Without it you'll see build warnings and run-time watermarks.

Why is my grid empty even though the request succeeds? Almost always JSON casing. See the first item in the common errors section.

Do I need DataSourceRequest for every action? You need it on any action that serves an Ajax-bound grid with server operations, because it carries the paging, sorting and filter state.

Should I return my entity classes directly? Avoid it. Use a view model, as in this tutorial, so you control exactly which fields reach the browser.

15. Conclusion and key takeaways

You now have a working project that binds a Kendo Grid four ways and supports full editing. The binding choice comes down to where you want paging, sorting and filtering to happen. Use server operations for large tables, and client operations only when the data is small enough to hold in the browser.

  • Install the Telerik wrapper, activate your license key, call AddKendo(), and turn off camelCase JSON before anything else.
  • For large tables, use Ajax binding and return a DataSourceResult from an action that accepts DataSourceRequest.
  • Match the HTTP verb between the grid and the action.
  • Project to a view model and keep queries as IQueryable so the database does the work.
  • Validate on the server and protect every action that reads or changes private data.

Codingvila provides articles and blogs on web and software development for beginners as well as free Academic projects for final year students in Asp.Net, MVC, C#, Vb.Net, SQL Server, Angular Js, Android, PHP, Java, Python, Desktop Software Application and etc.

If you have any questions, contact us on info.codingvila@gmail.com