Update record in grid does not not work with WebApiAdaptor

Query 1:
I have an issue in syncfusion blazor grid , that i am binding using Adaptor="Adaptors.WebApiAdaptor for SfDataManager
as following:
<sfgrid tvalue="CreateUpdateCustomerDto" allowpaging="true" toolbar="@(new string[] {" add","edit","delete","update","cancel"})"="" enablertl="true"></sfgrid<
<sfgrid tvalue="CreateUpdateCustomerDto" allowpaging="true" toolbar="@(new string[] {" add","edit","delete","update","cancel"})"="" enablertl="true"></sfgrid>
          
               
           
<grideditsettings allowadding="true" allowdeleting="true" allowediting="true" mode="EditMode.Normal"></grideditsettings>
<gridcolumns></gridcolumns>
<gridcolumn field="@nameof(CreateUpdateCustomerDto.Id)" headertext="Customer ID" isprimarykey="true" isidentity="true" isfrozen="true" textalign="@TextAlign.Right" validationrules="@(new { required=true, number=true})" headertextalign="@TextAlign.Right" width="140"></gridcolumn>
<gridcolumn field="@nameof(CreateUpdateCustomerDto.CustomerName)" headertext="Customer Name" validationrules="@(new { required=true})" width="150"></gridcolumn>
<gridcolumn field="@nameof(CreateUpdateCustomerDto.Telephone)" headertext="Telephone" edittype="EditType.DefaultEdit" width="140" textalign="@TextAlign.Right" headertextalign="@TextAlign.Right"></gridcolumn>
<gridcolumn field="@nameof(CreateUpdateCustomerDto.CellPhone)" headertext="CellPhone" edittype="EditType.DefaultEdit" validationrules="@(new { required=true})" format="d" textalign="TextAlign.Right" type="ColumnType.Date" width="160"></gridcolumn>
<gridcolumn field="@nameof(CreateUpdateCustomerDto.Website)" headertext="Website" edittype="EditType.DefaultEdit" width="150"></gridcolumn>
All crud ops runs ok except for the update or put request which is sent as following:
Request URL: https://localhost:44397/api/app/Customer
Request Method: PUT
Status Code: 405
Remote Address: [::1]:44397
The wrong with this request is that the id is not sent in the url , I think the url that grid generates should be as following
https://localhost:44397/api/app/Customer/1
where 1 is id of the record
what is wrong with my code please
I am using the latest version of the syncfusion blazor library 18.1.0.55


----------------------------------------------------------------
Query 2:
I have a production WebApi  and all the Get methods in its controllers return the data count in a property named "TotalCount" but your grid requires it to be named "Count"
I cannot modify the code of this API as it is production and used by other apps but I want to map this property  "TotalCount" to your property  "Count" in order for the paging of the grid to work directly
----------------------------------------------------------------
Query 3 : I have tried to do the previous crud ops using  CustomAdaptor and HttpClient but I do not know how to use the injected HttpClient instance into the "CustomAdaptor " class from  page class
Here is my code

@page "/customers2"
@inject HttpClient _httpClient

   
       
<SfGrid TValue="CustomerDto" AllowPaging="true" Toolbar="@(new string[] {"Add","Edit","Delete","Update","Cancel"})">
                <SfDataManager AdaptorInstance="@typeof(CustomAdaptor)" Adaptor="Adaptors.CustomAdaptor"></SfDataManager>
                <GridEditSettings AllowAdding="true" AllowEditing="true" AllowDeleting="true" Mode="EditMode.Normal"></GridEditSettings>
                <GridColumns>
                    <GridColumn Field=@nameof(CustomerDto.Id) HeaderText="Customer ID" IsPrimaryKey="true" TextAlign="@TextAlign.Right" ValidationRules="@(new { required=true, number=true})" HeaderTextAlign="@TextAlign.Right" Width="140"></GridColumn>
                    <GridColumn Field=@nameof(CustomerDto.CustomerName) HeaderText="Customer Name" ValidationRules="@(new { required=true})" Width="150"></GridColumn>
                    <GridColumn Field=@nameof(CustomerDto.Telephone) HeaderText="Telephone" EditType="EditType.NumericEdit" Width="140" TextAlign="@TextAlign.Right" HeaderTextAlign="@TextAlign.Right"></GridColumn>
                    <GridColumn Field=@nameof(CustomerDto.CellPhone) HeaderText="CellPhone" EditType="EditType.NumericEdit" TextAlign="TextAlign.Right" Width="160"></GridColumn>
                    <GridColumn Field=@nameof(CustomerDto.Website) HeaderText="Website" EditType="EditType.DefaultEdit" Width="150"></GridColumn>
                </GridColumns>
            </SfGrid>

       
   

@code{

    public static List<CustomerDto> Customers;// { get; set; };//  = CustomerDto.GetAllRecords();

    protected override async Task OnInitializedAsync()
    {
        GenericResult data = await _httpClient.GetFromJsonAsync("https://localhost:44397/api/app/Customer");

        if (data != null && data.Items != null)
            Customers = data.Items.Cast<CustomerDto>().ToList();

    }

    public class CustomAdaptor : DataAdaptor
    {
        public override async Task<Object> ReadAsync(DataManagerRequest dataManagerRequest, string key = null)
        {
            IEnumerable<CustomerDto> GridData = Customers;
            await Task.Delay(100); //To mimic asynchronous operation, we delayed this operation using Task.Delay
            if (dataManagerRequest.Skip != 0)
            {
                GridData = GridData.Skip(dataManagerRequest.Skip); //Paging
            }
            if (dataManagerRequest.Take != 0)
            {
                GridData = GridData.Take(dataManagerRequest.Take);
            }
            return dataManagerRequest.RequiresCounts ? new DataResult() { Result = GridData, Count = 100 /* CustomerDto.GetAllRecords().Count()*/ } : (object)GridData;
        }

        public override async Task<Object> InsertAsync(DataManager dataManager, object value, string key)
        {
            await Task.Delay(100); //To mimic asynchronous operation, we delayed this operation using Task.Delay
             //Customers.Insert(0, value as CustomerDto);

            // Get Error Here 
            await _httpClient.PostAsJsonAsync<CreateUpdateCustomerDto>("https://localhost:44397/api/app/Customer", value as CreateUpdateCustomerDto);
            return value;
        }

        public override async Task<object> RemoveAsync(DataManager dataManager, object value, string keyField, string key)
        {
            await Task.Delay(100); //To mimic asynchronous operation, we delayed this operation using Task.Delay
            int data = (int)value;
            Customers.Remove(Customers.Where((CustomerDto) => CustomerDto.Id == data).FirstOrDefault());
            return value;
        }

      
    }
}

9 Replies 1 reply marked as answer

RN Rahul Narayanasamy Syncfusion Team June 10, 2020 06:13 AM UTC

Hi Mohamed, 

Greetings from Syncfusion and Sorry for the delay. 

We have validated your query and you have faced difficulties while performing update operation. We have prepared a sample based on your requirement. And you want to map the TotalCount to Count property for performing paging operation.   

Normally while using WebApiAdaptor, it expects the response to be returned as Items and Count pair based on the RequiresCount. For binding data in Grid using WebApiAdaptor, you need to return data as Items and Count. Find the below documentation for your reference. 


If you want to customize your data binding and editing operations, then you can achieve your requirement by using Custom Binding. Find the below documentation for  your reference. 


Also, you have faced some difficulties while achieve this requirement by using CustomAdaptor. We have prepared a sample based on your requirement. Find the below code snippets and sample for your reference. 

 
<SfGrid TValue="Orders" AllowFiltering="true" Toolbar="@(new List<string> {"Add","Edit","Delete","Update","Cancel","Search" })" AllowSorting="true" AllowPaging="true"> 
    <GridFilterSettings Type="Syncfusion.Blazor.Grids.FilterType.Menu"></GridFilterSettings> 
    <SfDataManager AdaptorInstance="@typeof(CustomAdaptor)" Adaptor="Adaptors.CustomAdaptor"></SfDataManager> 
    <GridEditSettings AllowAdding="true" AllowDeleting="true" AllowEditing="true"></GridEditSettings> 
    <GridColumns> 
        <GridColumn Field="OrderID" HeaderText="Order ID" IsPrimaryKey="true" TextAlign="TextAlign.Right" Width="120"></GridColumn> 
        . . . 
   </GridColumns> 
</SfGrid> 
 
@code{ 
    static OrderService Ord { get; set; } 
    protected override void OnInitialized() 
    { 
        Ord = OrderData; 
    } 
    // Implementing custom adaptor by extending the DataAdaptor class 
    public class CustomAdaptor : DataAdaptor 
    { 
        // Performs data Read operation 
        public override async Task<object> ReadAsync(DataManagerRequest dm, string key = null) 
        { 
            IEnumerable<Orders> DataSource = await Ord.GetPeople(); 
            . . . 
           return dm.RequiresCounts ? new DataResult() { Result = DataSource, Count = count } : (object)DataSource; 
        } 
 
        //// Performs Insert operation 
        public override async Task<object> InsertAsync(DataManager dm, object value, string key) 
        { 
            await Ord.InsertOrderAsync(value as Orders); 
            return value; 
        } 
 
        //// Performs Remove operation 
        public override async Task<object> RemoveAsync(DataManager dm, object value, string keyField, string key) 
        { 
            await Ord.DeleteOrderAsync(value.ToString()); 
            return value; 
        } 
 
        //// Performs Update operation 
        public override async Task<object> UpdateAsync(DataManager dm, object value, string keyField, string key) 
        { 
            await Ord.UpdateOrderAsync((value as Orders).OrderID.ToString(), value as Orders); 
            return value; 
        } 
    } 
} 


Find the below sample for your reference. 


Please get back to us if you need further assistance. 

Regards, 
Rahul 


Marked as answer

MO Mohamed June 10, 2020 08:01 AM UTC

Hi Raul,
Many thanks for your reply

For  query 1, I did not understand what is the issue of the update operation using the "WebApiAdaptor"?

For Query 2,  Does this mean that I can not inject the HttpClient directly in the page without using an extra service class. I want to minimize the code written for these simple crud operations, for best code maintainability. Please confirm. Note I my project is Blazor WASM based project,  not Blazor Server project

New Query, Can I download the documentation site "https://blazor.syncfusion.com/documentation/" for offline?


RN Rahul Narayanasamy Syncfusion Team June 14, 2020 05:15 PM UTC

Hi Mohamed, 

Thanks for the update. 

Query: I did not understand what is the issue of the update operation using the "WebApiAdaptor"? 

We have validated the reported query and could you please share the network tab details(screenshot of the network tab while update operation) while performing update operation. It will be helpful to validate this problem and provide a better solution. 

Query: Does this mean that I can not inject the HttpClient directly in the page without using an extra service class. I want to minimize the code written for these simple crud operations, for best code maintainability. Please confirm. Note I my project is Blazor WASM based project,  not Blazor Server project  

We have validated your query and we have prepared a sample based on your requirement in client side project. Here, we have injected HttpClient directly into the  page and performed the CRUD operations. Find the below code snippets and sample for your reference. 

. . . 
@inject HttpClient Http 
 
<SfGrid TValue="Orders" AllowFiltering="true" Toolbar="@(new List<string> {"Add","Edit","Delete","Update","Cancel","Search" })" AllowSorting="true" AllowPaging="true"> 
    <GridFilterSettings Type="Syncfusion.Blazor.Grids.FilterType.Menu"></GridFilterSettings> 
    <SfDataManager AdaptorInstance="@typeof(CustomAdaptor)" Adaptor="Adaptors.CustomAdaptor"></SfDataManager> 
    . . . 
</SfGrid> 
 
@code{ 
    static HttpClient http { get; set; } 
   protected override void OnInitialized() 
    { 
        http = Http; 
    } 
    // Implementing custom adaptor by extending the DataAdaptor class 
    public class CustomAdaptor : DataAdaptor 
    { 
        // Performs data Read operation 
        public override async Task<object> ReadAsync(DataManagerRequest dm, string key = null) 
        { 
            //var orddata = await http.GetJsonAsync<List<Orders>>("api/Default"); 
            IEnumerable<Orders> DataSource = await http.GetJsonAsync<List<Orders>>("api/Default"); 
            . . . 
       } 
 
        //// Performs Insert operation 
        public override async Task<object> InsertAsync(DataManager dm, object value, string key) 
        { 
            await http.PostJsonAsync("api/Default/", value as Orders); 
            return value; 
        } 
 
        //// Performs Remove operation 
        public override async Task<object> RemoveAsync(DataManager dm, object value, string keyField, string key) 
        { 
            await http.DeleteAsync("api/Default/" + value.ToString()); 
            return value; 
        } 
 
        //// Performs Update operation 
        public override async Task<object> UpdateAsync(DataManager dm, object value, string keyField, string key) 
        { 
            await http.PutJsonAsync("api/Default/" + (value as Orders).OrderID, value as Orders); 
            return value; 
        } 
    } 
} 


Please get back to us if you need further assistance. 

Regards, 
Rahul 
 



MO Mohamed June 17, 2020 07:00 AM UTC

Dear Rahul,
Thanks for your info. I have tried the crud using the "CustomAdaptor" and injected "HttpClient" and its ok

Query 1 (Important)Can I download the documentation site "https://blazor.syncfusion.com/documentation/" for offline read?

Query 2: Returning back to the "WebApiAdaptor" issue, I have upgraded to the latest version of the "Syncfusion.Blazor" library version '18.1.0.57" and tried to test Crud using "WebApiAdaptor" but the update operation does not work at all, even no http request is sent to the server side. 
The server side PUT method signature  ( public object Put(int id, [FromBody] Orders value)).I also tried to change it to be ( public object Put([FromBody] Orders value)) but also nothing worked
Please find the attached project with issue mentioned , the issue in the page name "WebApiExample"

Query 3, I noticed that when editing a record in the grid and navigating to another row , the Update event is fired . I agree it should be fired with "Enter" key press only but others should be disable even using an option Can this behavior be changed?



Attachment: BlazorApp_DataGridUpdateWithApi_Issue_850a29d0.zip


RN Rahul Narayanasamy Syncfusion Team June 18, 2020 05:02 PM UTC

Hi Mohamed 

Query: Can I download the documentation site "https://blazor.syncfusion.com/documentation/" for offline read? 

We have validated your query and we do not expose the documentation source publicly so the offline documentation is not possible as of now. 

Query: tried to test Crud using "WebApiAdaptor" but the update operation does not work at all, even no http request is sent to the server side.  

We have validated your query and in your Put method you don’t need to pass id value as parameter. And you have provided UpdateUrl in SfDataManager which is not needed for update operation. Please change the below codes in your application. It will resolve the problem. find the below code snippets and sample for your reference. 

 
<SfGrid TValue="Orders" AllowFiltering="true" Toolbar="@(new List<string> { "Add", "Edit", "Delete", "Update", "Cancel", "Search" })" AllowSorting="true" AllowPaging="true"> 
    <GridFilterSettings Type="Syncfusion.Blazor.Grids.FilterType.Menu"></GridFilterSettings> 
    <SfDataManager Url="https://localhost:44354/api/Default/" 
                   UpdateUrl="https://localhost:44354/api/Default/" Adaptor="Adaptors.WebApiAdaptor"></SfDataManager>  //remove this 
    <GridEditSettings AllowAdding="true" AllowDeleting="true" AllowEditing="true" Mode="EditMode.Dialog"></GridEditSettings> 
    <GridColumns> 
        <GridColumn Field="OrderID" HeaderText="Order ID" IsPrimaryKey="true" TextAlign="TextAlign.Right" Width="120"></GridColumn> 
        <GridColumn Field="CustomerID" HeaderText="Customer Name" Width="150"></GridColumn> 
        <GridColumn Field="Freight" HeaderText="Freight" Format="C2" TextAlign="TextAlign.Right" EditType="EditType.NumericEdit" Width="120"></GridColumn> 
        <GridColumn Field="OrderDate" HeaderText="Freight" TextAlign="TextAlign.Right" EditType="EditType.DatePickerEdit" Type="ColumnType.DateTime" Width="120"></GridColumn> 
    </GridColumns> 
</SfGrid> 
 
@code{ 
//PUT: api/Default1/5 
        [HttpPut("{id}")] 
        public object Put(int id, [FromBody] Orders value)  //remove the highlighted codes. You don’t need to pass id 
        { 
            var data = order.Where(or => or.OrderID == value.OrderID).FirstOrDefault(); 
            . . . 
            return value; 
        } 

 
Query: I noticed that when editing a record in the grid and navigating to another row , the Update event is fired . I agree it should be fired with "Enter" key press only but others should be disable even using an option Can this behavior be changed?  

We have validated your query and could you please confirm which one is your requirement. It will be helpful to validate and provide a better solution 

  • Whether did you want to cancel the previously edited record state while clicking other rows?
Or 
  • Whether did you want to stay the edited state while  clicking other rows?

Regards, 
Rahul 



MO Mohamed July 13, 2020 06:29 PM UTC

Hi Rahul,
  • For my CRUD query using API, it is OK
  • For the Grid editing  , when the user clicks in other rows I suggest to add an option for grid contains both the following values:
  1.      cancel the previously edited record 
  2.      stay the edited state


RN Rahul Narayanasamy Syncfusion Team July 14, 2020 12:50 PM UTC

Hi Mohamed, 
 
Thanks for the update. 
 
Query: For the Grid editing  , when the user clicks in other rows I suggest to add an option for grid contains both the following values: cancel the previously edited record, stay the edited state 
 
We have validated your query and you want to prevent the save action while clicking other rows after editing the record. Here, we have prepared a sample based on your requirement using OnRecordClick, OnActionBegin and OnToolbarClick events with a boolean variable. Find the below code snippets and sample for your reference. 
 
 
    <SfGrid DataSource="@Orders" AllowPaging="true" Toolbar="@(new List<string>() { "Add""Edit""Delete""Cancel""Update" })" Height="315"> 
        <GridEditSettings AllowAdding="true" AllowEditing="true" AllowDeleting="true" Mode="EditMode.Normal"></GridEditSettings> 
        <GridEvents OnToolbarClick="ToolbarClickHandler" OnActionBegin="ActionBeginHandler" OnRecordClick="RecordClickHandler" TValue="Order"></GridEvents> 
        <GridColumns> 
            <GridColumn Field=@nameof(Order.OrderID) HeaderText="Order ID" IsPrimaryKey="true" ValidationRules="@(new ValidationRules{ Required=true})"  
TextAlign="TextAlign.Right" Width="120"></GridColumn> 
            . . . 
        </GridColumns> 
    </SfGrid> 
 
@code{ 
    public List<Order> Orders { getset; } 
 
    bool save = false;   //introducing Boolean value to achieve the requirement 
 
    . . . 
    public void RecordClickHandler(RecordClickEventArgs<Order> args) 
    { 
        save = true; 
    } 
    public void ActionBeginHandler(ActionEventArgs<Order> args) 
    { 
        if (save && args.RequestType == Syncfusion.Blazor.Grids.Action.Save) 
        { 
            args.Cancel = true;   //cancelling save action while clicking other rows 
        } 
    } 
    public void ToolbarClickHandler(Syncfusion.Blazor.Navigations.ClickEventArgs args) 
    { 
        if(args.Item.Text == "Update")  
        { 
            save = false; 
        } 
    } 
} 
 
 
 
Reference: 
 
Please get back to us if you need further assistance. 
 
Regards, 
Rahul 
 



AE Alvin Eng replied to Rahul Narayanasamy August 26, 2022 10:15 AM UTC

Hi, I've tried the sample provided, the update works find when the Mode="EditMode.Normal" or Mode="EditMode.Dialog" in GridEditSettings

However when I changed the  Mode="EditMode.Batch" in  GridEditSettings it doesn't update anymore, am I missing something?


<SfGrid TValue="Orders" AllowFiltering="true" Toolbar="@(new List<string> { "Add", "Edit", "Delete", "Update", "Cancel", "Search" })" AllowSorting="true" AllowPaging="true">

    <GridFilterSettings Type="Syncfusion.Blazor.Grids.FilterType.Menu"></GridFilterSettings>

    <SfDataManager Url="https://localhost:44354/api/Default/" Adaptor="Adaptors.WebApiAdaptor"></SfDataManager>

    <GridEditSettings AllowAdding="true" AllowDeleting="true" AllowEditing="true" Mode="EditMode.Batch"></GridEditSettings>

    <GridColumns>

        <GridColumn Field="OrderID" HeaderText="Order ID" IsPrimaryKey="true" TextAlign="TextAlign.Right" Width="120"></GridColumn>

        <GridColumn Field="CustomerID" HeaderText="Customer Name" Width="150"></GridColumn>

        <GridColumn Field="Freight" HeaderText="Freight" Format="C2" TextAlign="TextAlign.Right" EditType="EditType.NumericEdit" Width="120"></GridColumn>

        <GridColumn Field="OrderDate" HeaderText="Freight" TextAlign="TextAlign.Right" EditType="EditType.DatePickerEdit" Type="ColumnType.DateTime" Width="120"></GridColumn>

    </GridColumns>

</SfGrid>


Regards,

Alvin



NP Naveen Palanivel Syncfusion Team August 30, 2022 12:41 AM UTC

Hi Alvin,


Greetings from Syncfusion support.


We checked your query and  you are using WebApiAdaptor to bind data from Web API service to grid. When performing batch add/update/delete actions, the batch request will be send along with selected records. But unfortunately the ASP.NET Core Web API with batch is not yet supported by ASP.NET Core v3+. Hence it is not feasible from us to support batch edit with Web API, until ASP.NET Core provide the support for the proper batch handler.


Please find the general GitHub link below.

GitHub Link : https://github.com/aspnet/AspNetCore/issues/14722


Please get back to us if you need further assistance.


Regards,

Naveen Palanivel


Loader.
Up arrow icon