OData-v4 Binding
OData (Open Data Protocol) defines best practices when it comes to building and consuming RESTful APIs in a dependable manner. It enables you to mimic a Web API but with built-in support for filtering, selecting, and expanding amongst other capabilities. Binding the ASP.NET MVC TreeList through OData-v4 allows you to elevate its REST API by introducing advanced querying options.
For a runnable example, refer to the demo on OData binding of the TreeList component.
Installing the OData Package
To install the required dependencies for using OData, install the autonomous Microsoft.AspNet.OData
and Microsoft.AspNet.WebApi.WebHost
NuGet packages in your project.
Building the Edm Model and Configuring OData in an ASP.NET MVC Environment
To ensure that the application is configured for both WebApi and OData binding capabilities:
-
Configure Web API by calling
GlobalConfiguration.Configure
in theApplication_Start
method.public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { GlobalConfiguration.Configure(WebApiConfig.Register); RouteConfig.RegisterRoutes(RouteTable.Routes); } }
-
Create a file named
WebApiConfig.cs
inside theApp_Start
folder and configure the Edm model and OData services.public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Web API configuration and services. // Web API routes. config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); var builder = new ODataConventionModelBuilder(); builder.EntitySet<Product>("Products"); config.Count().Filter().OrderBy().Expand().Select().MaxTop(null); config.MapODataServiceRoute("odata", "odata", builder.GetEdmModel()); } }
Adding an OData Controller
To support writing and reading data using the OData formats, the ODataController
base class needs to be inherited for a given controller instance.
public class ProductsController : ODataController
{
...
}
Configuring the TreeList for OData-v4 Binding
To implement the OData binding within the boundaries of the Telerik UI for ASP.NET MVC TreeList, specify the .Type("odata-v4")
configuration method within the DataSource. This ensures that the requests can be sent to the OData endpoint in the expected format and out of the box.
@(Html.Kendo().TreeList<Product>()
.Name("treelist")
.Columns(columns =>
{
columns.Add().Field(f => f.ProductName).Width(250);
columns.Add().Field(e => e.UnitsInStock);
columns.Add().Field(e => e.Discontinued).Title("Ext");
})
.Filterable()
.Sortable()
.DataSource(ds => ds
.Custom()
.Type("odata-v4")
.Schema(sch => sch.Model(m =>
{
m.Id(i => i.ProductID);
m.Field("parentId", typeof(int?)).From("IsPartOf").DefaultValue(null);
}))
.Transport(t => t.Read(new { url = new Kendo.Mvc.ClientHandlerDescriptor() { HandlerName = "read" } }))
)
)
<script>
function read(dataItem){
if(!dataItem.hasOwnProperty('id')){
return "/odata/Products"
}
return `/odata/Products?key=${dataItem.id}`;
}
</script>
public class ProductsController : ODataController
{
private static List<Product> GetData() // Return Mocked Data.
{
return new List<Product>()
{
new Product {ProductID = 1, Discontinued = true, IsPartOf = null, hasChildren = true, ProductName = "Fruit", UnitPrice = 1254M},
new Product {ProductID = 2, Discontinued = true, IsPartOf = 1, hasChildren = false, ProductName = "Banana", UnitPrice = 1254M},
new Product {ProductID = 3, Discontinued = true, IsPartOf = null, hasChildren = true, ProductName = "Vegatables", UnitPrice = 1254M},
new Product {ProductID = 4, Discontinued = true, IsPartOf = 3, hasChildren = false, ProductName = "Potato", UnitPrice = 1254M},
};
}
// GET: odata/Products(5)
[EnableQuery]
public IQueryable<Product> GetProducts([FromODataUri] int? key)
{
List<Product> data = GetData();
data = data.Where(v => key.HasValue ? v.IsPartOf == key : v.IsPartOf == null)
.ToList();
return data.ToList();
}
}
public class Product
{
public int ProductID { get; set; }
public string ProductName { get; set; }
public decimal UnitPrice { get; set; }
public bool Discontinued { get; set; }
public int? IsPartOf { get; set; }
public bool hasChildren { get; set; }
}