Remote Data in ASP.Net Core Grid Component

4 Jun 202424 minutes to read

To bind remote data to grid component, assign service data as an instance of DataManager to the dataSource property. To interact with remote data source, provide the endpoint url.

<ejs-grid id="Grid">
    <e-data-manager url="http://services.odata.org/V4/Northwind/Northwind.svc/Orders/?$top=7" adaptor="ODataV4Adaptor" crossdomain="true"></e-data-manager>
    <e-grid-columns>
        <e-grid-column field="OrderID" headerText="Order ID" type="number" textAlign="Right" width="120"></e-grid-column>
        <e-grid-column field="CustomerID" headerText="Customer ID" type="string" width="140"></e-grid-column>
        <e-grid-column field="Freight" headerText="Freight" textAlign="Right" format="C2" width="120"></e-grid-column>
        <e-grid-column field="OrderDate" headerText="Order Date" format='yMd' textAlign="Right" width="140"></e-grid-column>
    </e-grid-columns>
</ejs-grid>
public IActionResult Index()
{
    return view(); 
}

NOTE

By default, DataManager uses ODataAdaptor for remote data-binding.

ExpandoObject with complex column binding using URL adaptor

You can achieve the ExpandoObject complex data binding in the data grid by using the dot(.) operator in the column.field. In the following examples, Customer.OrderDate, Customer.Freight, and Customer.ShipCountry are complex data.

The following code example shows how to bind ExpandoObject datasource in grid using URL adaptor.

<ejs-grid id="ExpandoObjectGrid" allowPaging="true" allowSorting="true" allowFiltering="true" toolbar="@(new List<string>() {"Add", "Edit", "Delete", "Update", "Cancel"})">
<e-grid-groupSettings showGroupedColumn="true" showDropArea="true"></e-grid-groupSettings>
    <e-data-manager url="/Home/UrlDataSource" adaptor="UrlAdaptor" insertUrl="/Home/Insert" updateUrl="/Home/Update" removeUrl="/Home/Remove"></e-data-manager>
            <e-grid-editSettings allowAdding="true" allowDeleting="true" allowEditing="true" mode="Normal" newRowPosition="Top"></e-grid-editSettings>
            <e-grid-columns>
                <e-grid-column field="OrderID" headerText="Order ID" isPrimaryKey="true" validationRules="@(new { required=true, number=true})" width="140"></e-grid-column>
                <e-grid-column field="Customer.OrderDate" headerText="Order Date" editType="datetimepickeredit" customFormat="@(new {type = "datetime", format = "M/d/y hh:mm a" })" width="160"></e-grid-column>
                <e-grid-column field="Customer.Freight" headerText="Freight" validationRules="@(new { required=true})" textAlign="Right" editType="numericedit" format="C2" width="140"></e-grid-column>
                <e-grid-column field="Customer.ShipCountry" headerText="Ship Country" editType="dropdownedit" width="150"></e-grid-column>
            </e-grid-columns>
</ejs-grid>
public static List<ExpandoObject> ExpandoOrders { get; set; } = new List<ExpandoObject>();

public IActionResult Index()
{
    getExpandoDatas();
    return View();
}

public IActionResult Insert([FromBody] CRUDModel<ExpandoObject> value)
{
    ExpandoOrders.Insert(0, value.Value);
    return Json(value.Value);
}

public IActionResult Update([FromBody] CRUDModel<ExpandoObject> value)
{
    if (value != null && value.Value != null)
    {
        var orderIDToUpdate = Convert.ToInt32(value.Key);

        var existingOrder = ExpandoOrders.FirstOrDefault(order =>
        {
            if (order is IDictionary<string, object> orderDictionary &&
                orderDictionary.TryGetValue("OrderID", out var orderIDValue))
            {
                var orderID = Convert.ToInt32(orderIDValue);
                return orderID == orderIDToUpdate;
            }

            return false;
        });

        if (existingOrder != null)
        {
            ExpandoOrders.Remove(existingOrder);
            ExpandoOrders.Insert(0, value.Value);
        }
    }

    return Json(value.Value);
}

public IActionResult Remove([FromBody] CRUDModel<ExpandoObject> value)
{
    if (value != null)
    {
        var orderIDToDelete = Convert.ToInt32(value.Key);

        var itemToRemove = ExpandoOrders.FirstOrDefault(order =>
        {
            if (order is IDictionary<string, object> dictionary &&
                dictionary.TryGetValue("OrderID", out var orderIDValue))
            {
                var orderID = Convert.ToInt32(orderIDValue);
                return orderID == orderIDToDelete;
            }

            return false;
        });

        if (itemToRemove != null)
        {
            ExpandoOrders.Remove(itemToRemove);
        }
    }

    return Json(value);
}

public class CRUDModel
{
    public List<ExpandoObject> Added { get; set; }
    public List<ExpandoObject> Changed { get; set; }
    public List<ExpandoObject> Deleted { get; set; }
    public ExpandoObject Value { get; set; }
    public int key { get; set; }
    public string action { get; set; }
}

public IActionResult UrlDataSource([FromBody] DataManagerRequest dm)
{
    IEnumerable DataSource = ExpandoOrders;
    DataOperations operation = new DataOperations();

    if (dm.Search != null && dm.Search.Count > 0)
    {
        DataSource = operation.PerformSearching(DataSource, dm.Search);  //Search
    }
    if (dm.Sorted != null && dm.Sorted.Count > 0) //Sorting
    {
        DataSource = operation.PerformSorting(DataSource, dm.Sorted);
    }
    if (dm.Where != null && dm.Where.Count > 0) //Filtering
    {
        DataSource = operation.PerformFiltering
            (DataSource, dm.Where, dm.Where[0].Operator);
    }
    int count = DataSource.Cast<ExpandoObject>().Count();
    if (dm.Select != null)
    {
        DataSource = operation.PerformSelect(DataSource, dm.Select);  
        DataSource = DataSource.Cast<ExpandoObject>().Distinct().AsEnumerable(); 
    }
    if (dm.Skip != 0)
    {
        DataSource = operation.PerformSkip(DataSource, dm.Skip);   //Paging
    }
    if (dm.Take != 0)
    {
        DataSource = operation.PerformTake(DataSource, dm.Take);
    }

    return dm.RequiresCounts ?
        Json(new { result = DataSource, count = count }) : Json(DataSource);
}

protected List<ExpandoObject> getExpandoDatas()
{
    string[] customerIDs = { "ALFKI", "ANANTR", "ANTON", "BLONP", "BOLID" };
    string[] shipCountrys = { "USA", "UK", "Denmark", "Australia", "India" };
    ExpandoOrders = Enumerable.Range(1, 5).Select((x) =>
    {
        dynamic d = new ExpandoObject();
        d.OrderID = 1000 + x;
        d.Customer = new ExpandoObject();
        d.Customer.Freight = (new double[] { 2, 1, 4, 5, 3 })[new Random().Next(5)] * x;
        d.Customer.OrderDate = (new DateTime[] { new DateTime(2010, 11, 5), new DateTime(2018, 10, 3), new DateTime(1995, 9, 9), new DateTime(2012, 8, 2), new DateTime(2015, 4, 11) })[new Random().Next(5)];
        d.Customer.ShipCountry = (new string[] { "USA", "UK", "Denmark", "Australia", "India" })[new Random().Next(5)];
        d.Freight = (new double[] { 2, 1, 4, 5, 3 })[new Random().Next(5)] * x;
        d.OrderDate = (new DateTime[] { new DateTime(2010, 11, 5), new DateTime(2018, 10, 3), new DateTime(1995, 9, 9), new DateTime(2012, 8, 2), new DateTime(2015, 4, 11) })[new Random().Next(5)];
        d.ShipCountry = (new string[] { "USA", "UK" })[new Random().Next(2)];
        d.Verified = (new bool[] { true, false })[new Random().Next(2)];
        return d;
    }).Cast<ExpandoObject>().ToList<ExpandoObject>();

    return ExpandoOrders;
}

Perform data and CRUD operations for complex ExpandoObject binding fields as well.

The following image represents ExpandoObject complex data binding.

Grid with ExpandoObject Binding

DynamicObject with complex column binding using URL adaptor

You can achieve DynamicObject complex data binding in the data grid by using the dot(.) operator in the column.field. In the following examples, Customer.OrderDate, Customer.Freight, and Customer.ShipCountry are complex data.

The following code example shows how to bind DynamicObject datasource in grid using URL adaptor.

<ejs-grid id="DynamicObjectGrid" allowPaging="true" allowSorting="true" allowFiltering="true" toolbar="@(new List<string>() {"Add", "Edit", "Delete", "Update", "Cancel"})">
<e-grid-groupSettings showGroupedColumn="true" showDropArea="true"></e-grid-groupSettings>
    <e-data-manager url="/Home/UrlDataSource" adaptor="UrlAdaptor" insertUrl="/Home/Insert" updateUrl="/Home/Update" removeUrl="/Home/Remove"></e-data-manager>
        <e-grid-editSettings allowAdding="true" allowDeleting="true" allowEditing="true" mode="Normal" newRowPosition="Top"></e-grid-editSettings>
        <e-grid-columns>
        <e-grid-column field="OrderID" headerText="Order ID" isPrimaryKey="true" validationRules="@(new { required=true, number=true})" width="140"></e-grid-column>
        <e-grid-column field="Customer.OrderDate" headerText="Order Date" editType="datetimepickeredit" customFormat="@(new {type = "datetime", format = "M/d/y hh:mm a" })" width="160"></e-grid-column>
        <e-grid-column field="Customer.Freight" headerText="Freight" validationRules="@(new { required=true})" textAlign="Right" editType="numericedit" format="C2" width="140"></e-grid-column>
        <e-grid-column field="Customer.ShipCountry" headerText="Ship Country" editType="dropdownedit" width="150"></e-grid-column>
        </e-grid-columns>
</ejs-grid>
public static List<DynamicDictionary> DynamicOrders { get; set; } = new List<DynamicDictionary>();

public IActionResult Index()
{
    getDynamicDatas();
    return View();
}

public IActionResult Insert([FromBody] CRUDModel<DynamicDictionary> value)
{
    DynamicOrders.Insert(0, value.Value);
    return Json(value.Value);
}

public IActionResult Update([FromBody] CRUDModel<DynamicDictionary> value)
{
    if (value != null && value.Value != null)
    {
        var orderIDToUpdate = Convert.ToInt32(value.Key);

        var existingOrder = DynamicOrders.FirstOrDefault(order =>
        {
            if (order is DynamicDictionary dynamicDict &&
                dynamicDict.dictionary.TryGetValue("OrderID", out var orderIDValue))
            {
                var orderID = Convert.ToInt32(orderIDValue);
                return orderID == orderIDToUpdate;
            }

            return false;
        });

        if (existingOrder != null)
        {
            DynamicOrders.Remove(existingOrder);
            DynamicOrders.Insert(0, value.Value);
        }
    }

    return Json(value.Value);
}

public IActionResult Remove([FromBody] CRUDModel<DynamicDictionary> value)
{
    if (value != null)
    {
        var orderIDToDelete = Convert.ToInt32(value.Key);
        var itemToRemove = DynamicOrders.FirstOrDefault(order =>
        {
            if (order is DynamicDictionary dynamicDict &&
                dynamicDict.dictionary.TryGetValue("OrderID", out var orderIDValue))
            {
                var orderID = Convert.ToInt32(orderIDValue);
                return orderID == orderIDToDelete;
            }

            return false;
        });


        if (itemToRemove != null)
        {
            DynamicOrders.Remove(itemToRemove);
        }
    }

    return Json(value);
}

 public class CRUDModel
{
    public List<DynamicDictionary> Added { get; set; }
    public List<DynamicDictionary> Changed { get; set; }
    public List<DynamicDictionary> Deleted { get; set; }
    public DynamicDictionary Value { get; set; }
    public int key { get; set; }
    public string action { get; set; }
}

public IActionResult UrlDataSource([FromBody] DataManagerRequest dm)
{
    IEnumerable DataSource = DynamicOrders;
    DataOperations operation = new DataOperations();

    if (dm.Search != null && dm.Search.Count > 0)
    {
        DataSource = operation.PerformSearching(DataSource, dm.Search);  //Search
    }
    if (dm.Sorted != null && dm.Sorted.Count > 0) //Sorting
    {
        DataSource = operation.PerformSorting(DataSource, dm.Sorted);
    }
    if (dm.Where != null && dm.Where.Count > 0) //Filtering
    {
        DataSource = operation.PerformFiltering
            (DataSource, dm.Where, dm.Where[0].Operator);
    }
    int count = DataSource.Cast<DynamicDictionary>().Count();
    if (dm.Select != null)
    {
        DataSource = operation.PerformSelect(DataSource, dm.Select);  
        DataSource = DataSource.Cast<DynamicDictionary>().Distinct().AsEnumerable(); 
    }
    if (dm.Skip != 0)
    {
        DataSource = operation.PerformSkip(DataSource, dm.Skip);   //Paging
    }
    if (dm.Take != 0)
    {
        DataSource = operation.PerformTake(DataSource, dm.Take);
    }

    return dm.RequiresCounts ?
        Json(new { result = DataSource, count = count }) : Json(DataSource);
}

protected List<DynamicDictionary> getDynamicDatas()
{
    string[] customerIDs = { "ALFKI", "ANANTR", "ANTON", "BLONP", "BOLID" };
    string[] shipCountrys = { "USA", "UK", "Denmark", "Australia", "India" };
    DynamicOrders = Enumerable.Range(1, 5).Select((x) =>
    {
        dynamic d = new DynamicList();
        d.OrderID = 1000 + x;
        d.Customer = new DynamicList();
        d.Customer.Freight = (new double[] { 2, 1, 4, 5, 3 })[new Random().Next(5)] * x;
        d.Customer.OrderDate = (new DateTime[] { new DateTime(2010, 11, 5), new DateTime(2018, 10, 3), new DateTime(1995, 9, 9), new DateTime(2012, 8, 2), new DateTime(2015, 4, 11) })[new Random().Next(5)];
        d.Customer.ShipCountry = (new string[] { "USA", "UK", "Denmark", "Australia", "India" })[new Random().Next(5)];
        d.Freight = (new double[] { 2, 1, 4, 5, 3 })[new Random().Next(5)] * x;
        d.OrderDate = (new DateTime[] { new DateTime(2010, 11, 5), new DateTime(2018, 10, 3), new DateTime(1995, 9, 9), new DateTime(2012, 8, 2), new DateTime(2015, 4, 11) })[new Random().Next(5)];
        d.ShipCountry = (new string[] { "USA", "UK" })[new Random().Next(2)];
        d.Verified = (new bool[] { true, false })[new Random().Next(2)];
        return d;
    }).Cast<DynamicDictionary>().ToList<DynamicDictionary>();

    return DynamicOrders;
}

public class DynamicDictionary : DynamicObject
{
    public Dictionary<string, object> dictionary = new Dictionary<string, object>();

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        string name = binder.Name;
        return dictionary.TryGetValue(name, out result);
    }
    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        dictionary[binder.Name] = value;
        return true;
    }

    public override System.Collections.Generic.IEnumerable<string> GetDynamicMemberNames()
    {
        return this.dictionary?.Keys;
    }

}

Perform data and CRUD operations for complex DynamicObject binding fields as well.

The following image represents DynamicObject complex data binding.

Grid with DynamicObject Binding

OData adaptor - Binding OData service

OData is a standardized protocol for creating and consuming data. You can retrieve data from OData service using the DataManager. Refer to the following code example for remote Data binding using OData service.

<ejs-grid id="Grid">
    <e-data-manager url="https://services.odata.org/V3/Northwind/Northwind.svc/Orders/" adaptor="ODataAdaptor" crossdomain="true"></e-data-manager>
    <e-grid-columns>
        <e-grid-column field="OrderID" headerText="Order ID" type="number" textAlign="Right" width="120"></e-grid-column>
        <e-grid-column field="CustomerID" headerText="Customer ID" type="string" width="140"></e-grid-column>
        <e-grid-column field="Freight" headerText="Freight" textAlign="Right" format="C2" width="120"></e-grid-column>
        <e-grid-column field="OrderDate" headerText="Order Date" format='yMd' textAlign="Right" width="140"></e-grid-column>
    </e-grid-columns>
</ejs-grid>
public IActionResult Index()
{
    return View();
}

OData v4 adaptor - Binding OData v4 service

The ODataV4 is an improved version of OData protocols, and the DataManager can also retrieve and consume OData v4 services. For more details on OData v4 services, refer to the OData documentation. To bind OData v4 service, use the ODataV4Adaptor.

<ejs-grid id="Grid">
    <e-data-manager url="http://services.odata.org/V4/Northwind/Northwind.svc/Orders/?$top=7" adaptor="ODataV4Adaptor" crossdomain="true"></e-data-manager>
    <e-grid-columns>
        <e-grid-column field="OrderID" headerText="Order ID" type="number" textAlign="Right" width="120"></e-grid-column>
        <e-grid-column field="CustomerID" headerText="Customer ID" type="string" width="140"></e-grid-column>
        <e-grid-column field="Freight" headerText="Freight" textAlign="Right" format="C2" width="120"></e-grid-column>
        <e-grid-column field="OrderDate" headerText="Order Date" format='yMd' textAlign="Right" width="140"></e-grid-column>
    </e-grid-columns>
</ejs-grid>
public IActionResult Index()
 { 
    return View();
 }

Odata with custom url

The Syncfusion ODataV4 adaptor extends support for calling customized URLs to accommodate data retrieval and CRUD actions as per your application’s requirements. However, when utilizing a custom URL with the ODataV4 adaptor, it’s essential to modify the routing configurations in your application’s route configuration file to align with your custom URL. You can invoke the custom URL by the following methods in the Datamanager

Configuring Custom URLs

To work with custom URLs for CRUD operations in the Syncfusion Grid, you can use the following properties:

  • insertUrl: Specifies the custom URL for inserting new records.
  • removeUrl: Specifies the custom URL for deleting records.
  • updateUrl: Specifies the custom URL for updating records.
  • batchUrl: Specifies the custom URL for batch editing operations.

Ensure that the routing configurations on the server-side are properly updated to handle these custom URLs.

The following code example describes the above behavior.

<ejs-grid id="Grid" toolbar="@(new List<string>() { "Add", "Edit", "Delete", "Update", "Cancel" })">
    <e-data-manager url="http://services.odata.org/V4/Northwind/Northwind.svc/Orders/?$top=7" adaptor="ODataV4Adaptor" 
    updateUrl= "https://localhost:xxxx/odata/Orders/Update" 
    insertUrl= "https://localhost:xxxx/odata/Orders/Insert"
    removeUrl= "https://localhost:xxxx/odata/Orders/Delete" 
    crossdomain="true"></e-data-manager>
    <e-grid-editSettings allowAdding="true" allowDeleting="true" allowEditing="true" mode="Normal"></e-grid-editSettings>
    <e-grid-columns>
        <e-grid-column field="OrderID" headerText="Order ID" type="number" textAlign="Right" width="120" isPrimaryKey="true" validationRules="@(new { required=true})"></e-grid-column>
        <e-grid-column field="CustomerID" headerText="Customer ID" type="string" width="140" validationRules="@(new { required=true, minLength=3})"></e-grid-column>
        <e-grid-column field="Freight" headerText="Freight" textAlign="Right" format="C2" width="120"></e-grid-column>
        <e-grid-column field="OrderDate" headerText="Order Date" format='yMd' textAlign="Right" width="140"></e-grid-column>
    </e-grid-columns>
</ejs-grid>

For batch editing, you can specify a custom batch URL as follows:

<ejs-grid id="Grid" toolbar="@(new List<string>() { "Add", "Edit", "Delete", "Update", "Cancel" })">
    <e-data-manager url="http://services.odata.org/V4/Northwind/Northwind.svc/Orders/?$top=7" adaptor="ODataV4Adaptor"
    BatchUrl="https://localhost:xxxx/odata/Orders/BatchUpdate"
    crossdomain="true"></e-data-manager>
    <e-grid-editSettings allowAdding="true" allowDeleting="true" allowEditing="true" mode="Batch"></e-grid-editSettings>
    <e-grid-columns>
        <e-grid-column field="OrderID" headerText="Order ID" type="number" textAlign="Right" width="120" isPrimaryKey="true" validationRules="@(new { required=true})"></e-grid-column>
        <e-grid-column field="CustomerID" headerText="Customer ID" type="string" width="140" validationRules="@(new { required=true, minLength=3})"></e-grid-column>
        <e-grid-column field="Freight" headerText="Freight" textAlign="Right" format="C2" width="120"></e-grid-column>
        <e-grid-column field="OrderDate" headerText="Order Date" format='yMd' textAlign="Right" width="140"></e-grid-column>
    </e-grid-columns>
</ejs-grid>

Web API adaptor

You can use WebApiAdaptor to bind grid with Web API created using OData endpoint.

<ejs-grid id="Grid">
    <e-data-manager url="/api/Orders" adaptor="WebApiAdaptor" crossdomain="true"></e-data-manager>
    <e-grid-columns>
        <e-grid-column field="OrderID" headerText="Order ID" type="number" textAlign="Right" width="120"></e-grid-column>
        <e-grid-column field="CustomerID" headerText="Customer ID" type="string" width="140"></e-grid-column>
        <e-grid-column field="Freight" headerText="Freight" textAlign="Right" format="C2" width="120"></e-grid-column>
        <e-grid-column field="OrderDate" headerText="Order Date" format='yMd' textAlign="Right" width="140"></e-grid-column>
    </e-grid-columns>
 </ejs-grid>
public class OrdersDetails
    {
        public static List<OrdersDetails> order = new List<OrdersDetails>();
        public OrdersDetails()
            {

            }
        public OrdersDetails(int OrderID, string CustomerId, int EmployeeId, double Freight, bool Verified, DateTime OrderDate, string ShipCity, string ShipName, string ShipCountry, DateTime ShippedDate, string ShipAddress)
            {
                this.OrderID = OrderID;
                this.CustomerID = CustomerId;
                this.EmployeeID = EmployeeId;
                this.Freight = Freight;
                this.ShipCity = ShipCity;
                this.Verified = Verified;
                this.OrderDate = OrderDate;
                this.ShipName = ShipName;
                this.ShipCountry = ShipCountry;
                this.ShippedDate = ShippedDate;
                this.ShipAddress = ShipAddress;
            }
        public static List<OrdersDetails> GetAllRecords()
        {
            if (order.Count() == 0)
            {
                int code = 10000;
                for (int i = 1; i < 5; i++)
                {
                    order.Add(new OrdersDetails(code + 1, "ALFKI", i + 0, 2.3 * i, false, new DateTime(1991, 05, 15), "Berlin", "Simons bistro", "Denmark", new DateTime(1996, 7, 16), "Kirchgasse 6"));
                    order.Add(new OrdersDetails(code + 2, "ANATR", i + 2, 3.3 * i, true, new DateTime(1990, 04, 04), "Madrid", "Queen Cozinha", "Brazil", new DateTime(1996, 9, 11), "Avda. Azteca 123"));
                    order.Add(new OrdersDetails(code + 3, "ANTON", i + 1, 4.3 * i, true, new DateTime(1957, 11, 30), "Cholchester", "Frankenversand", "Germany", new DateTime(1996, 10, 7), "Carrera 52 con Ave. Bolívar #65-98 Llano Largo"));
                    order.Add(new OrdersDetails(code + 4, "BLONP", i + 3, 5.3 * i, false, new DateTime(1930, 10, 22), "Marseille", "Ernst Handel", "Austria", new DateTime(1996, 12, 30), "Magazinweg 7"));
                    order.Add(new OrdersDetails(code + 5, "BOLID", i + 4, 6.3 * i, true, new DateTime(1953, 02, 18), "Tsawassen", "Hanari Carnes", "Switzerland", new DateTime(1997, 12, 3), "1029 - 12th Ave. S."));
                    code += 5;
                }
            }
            return order;
        }

            public int? OrderID { get; set; }
            public string CustomerID { get; set; }
            public int? EmployeeID { get; set; }
            public double? Freight { get; set; }
            public string ShipCity { get; set; }
            public bool Verified { get; set; }
            public DateTime OrderDate { get; set; }
            public string ShipName { get; set; }
            public string ShipCountry { get; set; }
            public DateTime ShippedDate { get; set; }
            public string ShipAddress { get; set; }
    }
namespace ApplicationName.Controllers
{
    [Produces("application/json")]
    [Route("api/Orders")]
    public class OrdersController : Controller
    {
        // GET: api/Orders
        [HttpGet]
        public object Get()
        {
            var queryString = Request.Query;
            var data = OrdersDetails.GetAllRecords().ToList();
            int skip = Convert.ToInt32(queryString["$skip"]);
            int take = Convert.ToInt32(queryString["$top"]);
            return take != 0 ? new { Items = data.Skip(skip).Take(take).ToList(), Count = data.Count() } : new { Items = data, Count = data.Count() };
        }
    }
}

The response object should contain properties, Items and Count, whose values are a collection of entities and total count of the entities, respectively.

The sample response object should look like this:

{
    Items: [{..}, {..}, {..}, ...],
    Count: 830
}

Remote save adaptor

You may need to perform all Grid Actions in client-side except the CRUD operations, that should be interacted with server-side to persist data. It can be achieved in Grid by using RemoteSaveAdaptor.

Datasource must be set to json property and set RemoteSaveAdaptor to the adaptor property. CRUD operations can be mapped to server-side using updateUrl, insertUrl, removeUrl, batchUrl, crudUrl properties.

You can use the following code example to use RemoteSaveAdaptor in Grid.

<ejs-grid id="Grid" toolbar="@(new List<string>() { "Add","Delete","Update", "Cancel" })">
    <e-data-manager json ="@ViewBag.dataSource" adaptor="RemoteSaveAdaptor" insertUrl="/Home/Insert" updateUrl="/Home/Update" removeUrl="/Home/Delete" ></e-data-manager>
    <e-grid-editSettings allowDeleting="true" allowEditing="true" allowAdding="true"></e-grid-editSettings>
    <e-grid-columns>
        <e-grid-column field="OrderID" headerText="Order ID" type="number"  textAlign="Right" width="120"></e-grid-column>
        <e-grid-column field="CustomerID" headerText="Customer ID" type="string" width="140"></e-grid-column>
        <e-grid-column field="Freight" headerText="Freight" textAlign="Right" format="C2" width="120"></e-grid-column>
        <e-grid-column field="OrderDate" headerText="Order Date" format='yMd' textAlign="Right" width="140"></e-grid-column>
    </e-grid-columns>
 </ejs-grid>
public ActionResult Index()
{
   var data = OrderRepository.GetAllRecords();
   ViewBag.dataSource = data;
   return View();
}

public ActionResult Update(EditableOrder value)
{
   var data = OrderRepository.Update(value);
   return Json(data, JsonRequestBehavior.AllowGet);
}

public ActionResult Insert(EditableOrder value)
{
   var data = OrderRepository.Add(value);
   return Json(data, JsonRequestBehavior.AllowGet);
}

public ActionResult Delete(int key)
{
   OrderRepository.Delete(key);
   return Json(key, JsonRequestBehavior.AllowGet);
}

Custom adaptor

You can create your own adaptor by extending the built-in adaptors. The following demonstrates custom adaptor approach and how to add a serial number for the records by overriding the built-in response processing using the processResponse method of the ODataAdaptor.

<ejs-grid id="CustomAdaptor" created="created">
    <e-grid-columns>
        <e-grid-column field="Sno" headerText="SNO" textAlign="Right" width="120"></e-grid-column>
        <e-grid-column field="CustomerID" headerText="Customer Name" width="150"></e-grid-column>
        <e-grid-column field="Freight" headerText="Freight" format="C2" width="120"></e-grid-column>
        <e-grid-column field="ShipCity" headerText="Ship City"></e-grid-column>
        <e-grid-column field="ShipCountry" headerText="Ship Country" width="150"></e-grid-column>
    </e-grid-columns>
</ejs-grid>

<script>
    function created(args) {
        class SerialNoAdaptor extends ej.data.ODataAdaptor {
            processResponse() {
                var i = 0;
                // calling base class processResponse function
                var original = super.processResponse.apply(this, arguments);
                // adding serial number
                original.result.forEach(function (item) { item['Sno'] = ++i });
                return { result: original.result, count: original.count };
            }
        }
        var grid = document.querySelector('#CustomAdaptor').ej2_instances[0];
        grid.dataSource = new ej.data.DataManager({
            url: "https://js.syncfusion.com/demos/ejServices/Wcf/Northwind.svc/Orders?$top=7",
            adaptor: new SerialNoAdaptor()
        });
    }
</script>
public IActionResult Index()
{
    return View();
}
public IActionResult Index()
{
    return View();
}

Offline mode

On remote data binding, all grid actions such as paging, sorting, editing, grouping, filtering, etc, will be processed on server-side. To avoid post back for every action, set the grid to load all data on initialization and make the actions process in client-side. To enable this behavior, use the Offline property of e-data-manager tag helper.

<ejs-grid id="Grid" allowPaging="true" allowSorting="true" allowGrouping="true">
    <e-data-manager url="http://services.odata.org/V4/Northwind/Northwind.svc/Orders" adaptor="ODataV4Adaptor" offline="true" crossdomain="true"></e-data-manager>
    <e-grid-pagesettings pageSize="7"></e-grid-pagesettings>
    <e-grid-columns>
        <e-grid-column field="OrderID" headerText="Order ID" type="number" textAlign="Right" width="120"></e-grid-column>
        <e-grid-column field="CustomerID" headerText="Customer ID" type="string" width="140"></e-grid-column>
        <e-grid-column field="Freight" headerText="Freight" textAlign="Right" format="C2" width="120"></e-grid-column>
        <e-grid-column field="OrderDate" headerText="Order Date" format='yMd' textAlign="Right" width="140"></e-grid-column>
    </e-grid-columns>
</ejs-grid>
public IActionResult Index()
{
    return View();
}

Handling on-demand grid actions

On-Demand grid actions help you to improve the performance for large data application. To achieve On-Demand in the grid, use UrlAdaptor. To define UrlAdaptor in the grid, specify the data service in url and the AdaptorType as UrlAdaptor like below.

<ejs-grid id="Grid" allowPaging="true" allowSorting="true" allowFiltering="true" toolbar="@(new List<string>() {"Search" })">
    <e-data-manager url="/Home/UrlDataSource" adaptor="UrlAdaptor"></e-data-manager>
    <e-grid-columns>
        <e-grid-column field="OrderID"  headerText="Order ID" isPrimaryKey="true" textAlign="Right" width="100"></e-grid-column>                
        <e-grid-column field="CustomerID" headerText="Customer ID" type="string" width="120"></e-grid-column>                
        <e-grid-column field="Freight" headerText="Freight" textAlign="Right" format="C2" editType="numericedit" width="120"></e-grid-column>                               
        <e-grid-column field="ShipCountry" headerText="Ship Country" width="150"></e-grid-column>                
    </e-grid-columns>
</ejs-grid>

After defined DataManager, grid will request an AJAX POST for data. It will be sent to the specified data service for every grid actions with the needed parameters. This query parameters will help you to perform server-side operations for grid.

Parameters Description
RequiresCounts If it is true then the total count of records will be included in response.
Skip Holds the number of records to skip.
Take Holds the number of records to take.
Sorted Contains details about current sorted column and its direction.
Table Defines data source table name.
Where Contains details about current filter column name and its constraints.

The parameters of DataManager bound to DataManagerRequest in the server. You can use Dataoperations and DataManagerRequest to process grid actions such as Paging, Sorting, Searching, and Filtering using the following methods.

Method Names Actions
PerformSkip Bypasses a specified Skip value and returns the remaining collections of records.
PerformTake Bypasses a specified Take value and returns the remaining collections of records.
PerformFiltering Filters a sequence of records based on a predicate.
PerformSorting Sorts the collections of records based on its direction.
PerformSearching Search the records based on a predicate.
public IActionResult UrlDatasource([FromBody]DataManagerRequest dm)
{
    IEnumerable DataSource = OrdersDetails.GetAllRecords();
    DataOperations operation = new DataOperations();
    if (dm.Search != null && dm.Search.Count > 0)
    {
        DataSource = operation.PerformSearching(DataSource, dm.Search);  //Search
    }
    if (dm.Sorted != null && dm.Sorted.Count > 0) //Sorting
    {
        DataSource = operation.PerformSorting(DataSource, dm.Sorted);
    }
    if (dm.Where != null && dm.Where.Count > 0) //Filtering
    {
        DataSource = operation.PerformFiltering(DataSource, dm.Where, dm.Where[0].Operator);
    }
    int count = DataSource.Cast<OrdersDetails>().Count();
    if (dm.Skip != 0)
    {
        DataSource = operation.PerformSkip(DataSource, dm.Skip);   //Paging
    }
    if (dm.Take != 0)
    {
        DataSource = operation.PerformTake(DataSource, dm.Take);
    }
    return dm.RequiresCounts ? Json(new { result = DataSource, count = count }) : Json(DataSource);
}

NOTE

If the grid rendered rows with empty/blank values then it can be resolved with the procedure explained here.

WebMethod

The WebMethodAdaptor is used to bind datasource from remote services and code behind methods. It can be enabled in Grid using Adaptor property of DataManager as WebMethodAdaptor.

For every operations, an AJAX post will be send to the specified data service.

<ejs-grid id="Grid">
    <e-data-manager url="Default.aspx/DataSource" adaptor="WebMethodAdaptor" crossdomain="true"></e-data-manager>
    <e-grid-columns>
        <e-grid-column field="OrderID" headerText="Order ID" type="number" textAlign="Right" width="120"></e-grid-column>
        <e-grid-column field="CustomerID" headerText="Customer ID" type="string" width="140"></e-grid-column>
        <e-grid-column field="Freight" headerText="Freight" textAlign="Right" format="C2" width="120"></e-grid-column>
        <e-grid-column field="OrderDate" headerText="Order Date" format='yMd' textAlign="Right" width="140"></e-grid-column>
    </e-grid-columns>
 </ejs-grid>
public IActionResult Index()
{
    
    return View();
}

WebMethodAdaptor expects JSON response from the server and the response object should contain properties result and count whose values are collection of entities and total count of the entities respectively.

The sample response object should look like below.

{
    result: [{..}, {..}, {..}, ...],
    count: 830
}

NOTE

The controller method’s data parameter name must be value.