New to Telerik UI for ASP.NET MVCStart a free 30-day trial

Bind Grid to OData-v4 Using WebAPI Controller

Environment

ProductTelerik UI for ASP.NET MVC Grid
Product version2025.1.227

Description

How can I bind the Grid to OData-v4 using WebAPI endpoints?

Solution

OData-v4 is not fully supported, and the approaches for processing DateTime properties are limited.

The reason is that WebAPI does not support the DateTime data type any more. Instead, WebAPI now uses the DateTimeOffset type as a main data type when it comes to dates. However, to keep information for both date and offset, the DateTimeOffet requires the Model (that the DataSource creates), which is not possible with the current architecture of the DataSource and Model.

You can achieve this requirement using the following implementation:

  1. Configure the Grid's DataSource as per the example below:

    Razor
        .DataSource(dataSource => dataSource
            .Custom()
            .Batch(true)
            .Schema(sch =>
            {
                sch.Model(m=>{
                    m.Id("ProductID");
                    m.Field(f=>f.ProductID).Editable(false);
                    m.Field("UnitPrice", typeof(Decimal));
                });
            })
            .Type("odata-v4")
            .Transport(t =>
            {
                t.Read(new { url = new Kendo.Mvc.ClientHandlerDescriptor() { HandlerName = "readProduct" } });
                t.Update(new { url = new Kendo.Mvc.ClientHandlerDescriptor() { HandlerName = "updateProduct" } });
                t.Create(new { url = new Kendo.Mvc.ClientHandlerDescriptor() { HandlerName = "createProduct" } });
                t.Destroy(new { url = new Kendo.Mvc.ClientHandlerDescriptor() { HandlerName = "destroyProduct" } });
                t.Batch(new { url = new Kendo.Mvc.ClientHandlerDescriptor() { HandlerName = "batchProduct" } });
            })
            .PageSize(20)
            .ServerPaging(true)
            .ServerSorting(true)
            .ServerFiltering(true)
        )
  2. Define the JavaScript functions that return the respective URL:

    JS
        function batchProduct() {
            return "https://demos.telerik.com/kendo-ui/service-v4/odata/$batch";
        }
    
        function readProduct() {
            return "https://demos.telerik.com/kendo-ui/service-v4/odata/Products";
        }
    
        function updateProduct(dataItem) {
            return "https://demos.telerik.com/kendo-ui/service-v4/odata/Products(" + dataItem.ProductID + ")";
        }
    
        function createProduct(dataItem) {
            delete dataItem.ProductID;
            return "https://demos.telerik.com/kendo-ui/service-v4/odata/Products";
        }
    
        function destroyProduct(dataItem) {
            return "https://demos.telerik.com/kendo-ui/service-v4/odata/Products(" + dataItem.ProductID + ")";
        }
  3. Set up the ODataController:

    C#
     public class ODataWebApiWrappersProductsController : ODataController
    {
        private ODataWebApiWrappersEntities db = new ODataWebApiWrappersEntities();
    
        // GET: odata/ODataWebApiWrappersProducts
        [EnableQuery]
        public IQueryable<ODataWebApiWrappersProduct> GetProducts()
        {
            return db.Products;
        }
    
        // PUT: odata/ODataWebApiWrappersProducts(5)
        public IHttpActionResult Put([FromODataUri] int key, ODataWebApiWrappersProduct product)
        {   
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
    
            if (key != product.ProductID)
            {
                return BadRequest();
            }
    
            db.Products.Attach(product);
            db.Entry(product).State = EntityState.Modified;
    
            try
            {
                db.SaveChanges();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!ProductExists(key))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }
    
            return Updated(product);
        }
    
        public IHttpActionResult Post(ODataWebApiWrappersProduct product)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
            db.Products.Add(product);
            //db.SaveChanges();
            return Created(product);
        }
    
        private bool ProductExists(int key)
        {
            return db.Products.Count(e => e.ProductID == key) > 0;
        }
    }

To review the complete example, refer to the project on how to configure the Grid's DataSource to communicate with the WebAPI Controller through the OData-v4 protocol.

More ASP.NET MVC Grid Resources

See Also