ASP.NET CORE backend implementation for WebApiAdaptor (not UrlAdaptor)??

Hi,
I would like to ask, what's the best way to handle the WebApiAdaptor requests in a ASP.NET CORE backend? I've seen there is the UrlApiAdaptor, but I don't like this implementation of using the body for all request details. Is there any .NET library, that solves the WebApi request using Linq? I would like to use the typical web api request standards (GET, POST, PUT, DELETE) to handle the various requests and solve them with Linq. Basically, I just need something, that translates me the whole API request in a useable "C# code", on which I can later build a LINQ query, and finally response the request.

7 Replies 1 reply marked as answer

MS Manivel Sellamuthu Syncfusion Team April 13, 2021 07:25 AM UTC

Hi Laurin, 
 
Greetings from Syncfusion support. 
 
By using Webapi adaptor, you can handle the webAPi services. When using this, the response object should contain properties Items and Count whose values are a collection of entities and the total count of the entities respectively. This can be explained in the below documentation. 
 
 
By default, the GET method will be triggered when we perform Paging, Sorting, Filtering, Exporting, etc., in the Grid. Here, you can perform the actions based on the queries and return the object with Items and Count format. 
 
Screenshot #1: 
 
 
When perform Insert, Update and Delete actions in the Grid triggers the POST, PUT, DELETE method with correspond data and return the value to the Grid. 
 
Screenshot #2: Insert action 
 
 
Screenshot #3: Update action 
 
 
Screenshot #4: Delete action 
 
 
 
Code example in the backend:  
 
 
public class OrdersController : Controller 
    { 
        // GET: api/Orders 
        [HttpGet] 
 
        public object Get()  // triggered when perform paging, Sorting, Filtering etc., 
        { 
            var queryString = Request.Query; 
 
            int skip = Convert.ToInt32(queryString["$skip"]); 
            int take = Convert.ToInt32(queryString["$top"]); 
            var data = OrdersDetails.GetAllRecords().ToList(); 
            return take != 0 ? new { Items = data.Skip(skip).Take(take).ToList(), Count = data.Count() } : new { Items = data, Count = data.Count() };  // return the data in Items and Count format 
 
        } 
 
        // GET: api/Orders/5 
        [HttpGet("{id}", Name = "Get")] 
        public string Get(int id) 
        { 
            return "value"; 
        } 
 
        // POST: api/Orders 
        [HttpPost] 
        public object Post([FromBody]OrdersDetails value) // triggered when perform insert action 
        { 
            OrdersDetails.GetAllRecords().Insert(0, value); 
            return value; 
        } 
 
 
        // PUT: api/Orders/5 
        [HttpPut] 
        public object Put(int id, [FromBody]OrdersDetails value) // triggered when perform update action 
        { 
            var data = OrdersDetails.GetAllRecords().Where(or => or.OrderID == value.OrderID).FirstOrDefault(); 
            if (data != null) 
            { 
                var ord = value; 
                OrdersDetails val = OrdersDetails.GetAllRecords().Where(or => or.OrderID == ord.OrderID).FirstOrDefault(); 
                val.OrderID = ord.OrderID; 
                val.EmployeeID = ord.EmployeeID; 
               val.CustomerID = ord.CustomerID; 
                val.Freight = ord.Freight; 
                val.OrderDate = ord.OrderDate; 
                val.ShipCity = ord.ShipCity; 
            } 
            return value; 
        } 
 
        // DELETE: api/ApiWithActions/5 
        [HttpDelete("{id:int}")] 
        [Route("Orders/{id:int}")] 
        public void Delete(int id) // triggered when perform delete action 
        { 
            var data = OrdersDetails.GetAllRecords().Where(or => or.OrderID == id).FirstOrDefault(); 
            OrdersDetails.GetAllRecords().Remove(data); 
        } 
 
         
    } 
    public class Data 
    { 
        public bool requiresCounts { get; set; } 
        public int skip { get; set; } 
        public int take { get; set; } 
    } 
 
Please get back to us if you need further assistance with this. 
 
Regards, 
Manivel 


Marked as answer

LS Laurin S April 13, 2021 07:41 AM UTC

Thanks for your quick reply.

Your answer is completely clear, but how can I convert a filter or search request to a linq query?

For example, how can I convert the following 2 GET requests to a linq query to response in the requested way?

https://localhost:5001/api/countries/?where=(Id%20eq%20guid%2700000000-0000-0000-0000-000000000001%27)%20or%20(Id%20eq%20guid%2700000000-0000-0000-0000-000000000002%27)

OR

GET https://localhost:5001/api/users/?count=allpages&where=(substringof(%27max%27,tolower(LastName)))%20or%20(substringof(%27max%27,tolower(FirstName)))%20or%20(substringof(%27max%27,tolower(CodiceFiscale)))%20or%20(substringof(%27max%27,tolower(CentroDiCosto)))%20or%20(substringof(%27max%27,tolower(DateOfBirth)))%20or%20(substringof(%27max%27,tolower(PlaceOfBirth)))%20or%20((NationalityId%20eq%20guid%2700000000-0000-0000-0000-000000000001%27)%20or%20(NationalityId%20eq%20guid%2700000000-0000-0000-0000-000000000002%27)%20or%20(NationalityId%20eq%20guid%27849b2f06-2571-42ab-bdba-00e0100048d7%27)%20or%20(NationalityId%20eq%20guid%27b76d6e76-7e22-4a7d-ac0a-01ba90d673f9%27)%20or%20(NationalityId%20eq%20guid%279572e54a-95cc-4b70-b081-0378ebfe619f%27))%20or%20(substringof(%27max%27,tolower(Password)))%20or%20(substringof(%27max%27,tolower(Phone)))%20or%20(substringof(%27max%27,tolower(EMail)))%20or%20(substringof(%27max%27,tolower(Comment)))%20or%20(substringof(%27max%27,tolower(DateAdded)))&orderBy=LastName,FirstName&onlyActive=true&skip=0&take=20


Thanks.



MS Manivel Sellamuthu Syncfusion Team April 14, 2021 11:24 AM UTC

Hi Laurin, 

Thanks for your update. 

By default grid sends request to the server for all the grid actions like Paging, Sorting, Filtering, Searching etc., in the controller side you need to perform these operation with the correspond queries and return the data with Items & Count value pair which will be bound to the grid. This is demonstrated in the below code example. 

public class OrderController : ApiController 
    { 
        // GET: api/Order 
        public object Get()  // for every grid action (Paging, Sorting, Searching, etc.,) Get() method is called 
        { 
             
            var queryString = System.Web.HttpContext.Current.Request.QueryString; // get the current grid query 
 
// perform the operation using the query 
            int skip = Convert.ToInt32(queryString["$skip"]); //paging 
            int take = Convert.ToInt32(queryString["$top"]); 
            string filter = queryString["$filter"]; // filtering  
            string sort = queryString["$orderby"]; // sorting  
            var data = OrdersDetails.GetAllRecords(); 
            List<OrdersDetails> Datae = new List<OrdersDetails>(); 
            List<OrdersDetails> Datase = new List<OrdersDetails>(); 
            if (sort != null) //Sorting 
            { 
                switch (sort) 
                { 
                    case "OrderID": 
                        if (sort.Substring(sort.IndexOf(' ') + 1) != null) 
                            data = data.OrderByDescending(x => x.OrderID).ToList(); 
                        else 
                            data = data.OrderBy(x => x.OrderID).ToList(); 
                        break; 
                    case "CustomerID": 
                        if (sort.Substring(sort.IndexOf(' ') + 1) != null) 
                            data = data.OrderByDescending(x => x.CustomerID).ToList(); 
                        else 
                            data = data.OrderBy(x => x.CustomerID).ToList(); 
                        break; 
. . . 
                } 
            } 
if (filter != null) // to handle filter opertaion 
{ 
    if (filter.Contains("substring"))//searching  
    { 
        var key = filter.Split(new string[] { "'" }, StringSplitOptions.None)[1]; 
        data = data.Where(fil => fil.CustomerID.ToString().Contains(key.ToUpper()) 
                                || fil.EmployeeID.ToString().Contains(key) 
                                || fil.Freight.ToString().Contains(key) 
                                || fil.OrderID.ToString().Contains(key)); 
    } 
    else 
    { 
    var newfiltersplits = filter; 
    var filtersplits = newfiltersplits.Split('(', ')', ' '); 
    var filterfield = ""; 
    var filtervalue = ""; 
 
    if (filtersplits.Length == 7) 
    { 
        filterfield = filtersplits[1]; 
        filtervalue = filtersplits[3]; 
        if (filtersplits[2] == "tolower") 
        { 
            filterfield = filter.Split('(', ')', '\'')[3]; 
            filtervalue = filter.Split('(', ')', '\'')[5]; 
        } 
    } 
. . 
. . 
. .   
    switch (filterfield) 
    { 
   case "CustomerID": 
        data = (from cust in data 
                where cust.CustomerID.ToLower().StartsWith(filtervalue.ToString()) 
                select cust); 
        break; 
       . . 
       . . 
   } 
// return the data with Items and Count pair 
            return new 
            { 
                Items = data.Skip(skip).Take(take), 
                Count = data.Count() 
                //  return order; 
            }; 
        } 

Please let us know if you need further assistance. 

Regards, 
Manivel 



LS Laurin S April 14, 2021 12:09 PM UTC

Ok, such a solution is clear to me. 

But is there any automatic solution? 
The facts are as follows, since it's basically always the same scenario. For each api request we have a controller. In each controller there is a Linq query, that collects the data from the database. Normally, all records are return and the job is done (assuming no pagination). Now some times, the grid makes a "special" requests. For example, the grid asks for a change in the sorting, filtering, pagination, or similar. However, this type of "special" requests are always of the same form. Therefore, it would be of good software development practice, if a library/helper method would do that for us automatically (based on the "normal" original query), independently on the current api controller or query.

Demo Example:

        public async Task<ActionResult<IEnumerable<object>>> GetUsersWithoutCourses()
        {
            var result = from u in _context.Users
                         select new
                         {
                             u.Id,
                             u.FirstName,
                             u.LastName,
                             u.CodiceFiscale,
                             u.CentroDiCosto,
                             u.DateAdded,
                             u.DateOfBirth,
                             u.PlaceOfBirth,
                             u.NationalityId,
                             u.Phone,
                             u.EMail,
                             u.Comment
                         };

            return await this.ApplyGridRequirements(result); 
        }

 public static async Task<ActionResult> ApplyGridRequirements(IQueryable data)
        {
            var query = this.Request.Query;

            string orderBy = query["orderBy"].ToString();
            if (!string.IsNullOrWhiteSpace(orderBy))
                data = data.OrderBy(orderBy); <---- sorting should be done automatically

            string where = query["where"].ToString();
            if (!string.IsNullOrWhiteSpace(where))
                data = data.Where(where); //<---- filtering should be done automatically

            int count = data.Count();

            int skip = query["skip"].ToString().ToInt(0);
            if (skip > 0)
                data = data.Skip(skip);

            int take = query["take"].ToString().ToInt(0);
            if (take > 0)
                data = data.Take(take);

            List<dynamic> result = await data.ToDynamicListAsync();
            return query["count"].ToString()?.ToLower() == "allpages"
                ? controllerBase.Ok(new { Items = result, Count = count })
                : controllerBase.Ok(result);
        }



Your proposed solution is completely correct, but much too individual for a single api request. i cannot solve this individually for every api request. an automatic solution is needed (a similar solution like the following for the UrlAdaptor, but with the syntax/technique/method of the WebApiAdaptor: https://ej2.syncfusion.com/aspnetcore/Grid/UrlAdaptor#/material).

Thanks.


MS Manivel Sellamuthu Syncfusion Team April 19, 2021 10:52 AM UTC

Hi Laurin, 

Thanks for your update. 

The WebAPI adaptor is one of the Microsoft data services and it is an extensible framework for building HTTP based services.  

For WebAPI adaptor we will send the queries based on the Web API standard protocols and you need to handle the Data operations (Sorting, Filtering) in a Microsoft way. You can use ODataQueryOptions which is Microsoft DLL which is used to perform the sorting/filtering operation.  

Please refer the below code example and  general link for more information. 

 
    public class OrderController : ApiController 
    { 
        // GET: api/Order 
        public PageResult<OrdersDetailsGet(ODataQueryOptions opts) 
        { 
            var results = OrdersDetails.GetAllRecords().AsQueryable(); 
            var count = results.Count(); 
            if (opts.OrderBy != null) 
                results = opts.OrderBy.ApplyTo(results); 
            if (opts.Filter != null) 
            { 
                    results = opts.Filter.ApplyTo(results, new ODataQuerySettings()).Cast<OrdersDetails>(); 
            } 
            if (opts.InlineCount != null) 
                count = results.Count(); 
            if (opts.Skip != null) 
                results = opts.Skip.ApplyTo(results, new ODataQuerySettings()); 
            if (opts.Top != null) 
                results = opts.Top.ApplyTo(results, new ODataQuerySettings()); 
            return new PageResult<OrdersDetails>(results, null, count); 
        } 


The URL Adaptor which is a inbuilt Syncfusion Adaptor and for this adaptor we have defined a class named DataOperations  to perform the grid actions such as Paging, Sorting, and Filtering using helper methods. 

Please let us know if you need further assistance. 

Regards, 
Manivel 



LS Laurin S April 19, 2021 12:47 PM UTC

Ok. Anyway, for me it doesn't seem to be the best solution or best state-of-the-art solution. OData is extremely old, UrlAdaptor does not exists for other backends, REST API would be the standard and is well known through many programming languages... therefore it's also widely used and appears to be the current state-of-the-art.

Thanks anyway. (thread closed)


MS Manivel Sellamuthu Syncfusion Team April 20, 2021 02:32 PM UTC

Hi Laurin, 

Thanks for your update. 

In previous update we explained that the Web API adaptor is created based on the Microsoft standard protocols which uses GET, PUT, POST, DELETE requests for corresponding server interactions. To perform the server actions for that Web API adaptor we need to use either manual code or Odata Query Options only.  

The URL adaptor is a Syncfusion created customized adaptor(uses POST request for all grid actions), for that we have defined a helper class to handle the grid actions. So we suggest you to use any of the suggested solution to achieve your requirement. 

Regards, 
Manivel 


Loader.
Up arrow icon