Open Closed

Error calling Service method with DataSourceRequest parameter #1060


User avatar
0
Leonardo.Willrich created
  • ABP Framework version: v4.2.2
  • UI type: Angular / MVC / Blazor
  • DB provider: EF Core
  • Tiered (MVC) or Identity Server Separated (Angular): yes / no
  • Exception message and stack trace:
  • Steps to reproduce the issue:

Hi,

I am using Telerik components in my project and I am trying to use a Server Filtering. To be able to do that, I need to send a parameter from Blazor side to Application Service to filter/sort/group my records as per the grid parameters.

It is working fine, but, when I add some filter, it is raising an exception on Blazor side before calling the method in the Application Service. I am struggling to understand what is wrong with that.

Here is the error in the console:

My Application Service:

 [HttpPost]
public async Task<DataEnvelope<OutageReportDto>> GetOutageReportList([FromBody] DataSourceRequest request)
{
    try
    {
        var time = DateTime.UtcNow.AddDays(-1);
        var outageList = _outageReportRepository
            .Include(x => x.SupplyNetwork).Include(x => x.Cause)
            .Where(x => x.TenantId == CurrentTenant.Id.Value)
            .Where(x => x.RecordedTime >= time)
            .Select(x => ObjectMapper.Map<OutageReport, OutageReportDto>(x)).ToList();

        var output = await outageList.ToDataSourceResultAsync(request);

        DataEnvelope<OutageReportDto> dataToReturn;

        if (request.Groups != null && request.Groups.Count > 0)
        {
            // If there is grouping, use the field for grouped data
            // The app must be able to serialize and deserialize it
            // Example helper methods for this are available in this project
            // See the GroupDataHelper.DeserializeGroups and JsonExtensions.Deserialize methods
            dataToReturn = new DataEnvelope<OutageReportDto>
            {
                GroupedData = output.Data.Cast<AggregateFunctionsGroup>().ToList(),
                TotalItemCount = output.Total
            };
        }
        else
        {
            // When there is no grouping, the simplistic approach of 
            // just serializing and deserializing the flat data is enough
            dataToReturn = new DataEnvelope<OutageReportDto>
            {
                CurrentPageData = output.Data.Cast<OutageReportDto>().ToList(),
                TotalItemCount = output.Total
            };
        }

        return dataToReturn;
    }
    catch (Exception ex)
    {
        Console.WriteLine("Error: ", ex.Message);
        Console.WriteLine(ex);
        throw;
    }
}

My Blazor page code behind code:

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;
using Telerik.Blazor.Components;
using Telerik.DataSource;
using TVD_Holdings_Ltd.AvalancheOCP.AOR.OutageReports;

namespace TVD_Holdings_Ltd.AvalancheOCP.Blazor.Pages.AOR
{
    public class OutageReportGridBase : ComponentBase
    {
        [Inject] IOutageReportAppService outageReportService { get; set; }
        
        [Inject] IConfiguration Configuration { get; set; }
        [Inject] HttpClient httpClient { get; set; }
        //
        protected TelerikGrid<object> MainGrid { get; set; }
        protected List<object> OutageReportList { get; set; }
        protected int Total { get; set; }
        //
        private HubConnection hubConnection;
        private DataSourceRequest LastRequest;
        

        protected override async Task OnInitializedAsync()
        {
            var baseUrl = Configuration.GetValue<string>("RemoteServices:Default:BaseUrl");
            hubConnection = new HubConnectionBuilder()
           .WithUrl(baseUrl + "/aor")
           .Build();

            hubConnection.On("OutagesUpdated", LoadData);

            await hubConnection.StartAsync();
        }

        protected override async Task OnAfterRenderAsync(bool firstRender)
        {
            if (firstRender)
            {
                await SetGridDefaultSort();
            }
            await base.OnAfterRenderAsync(firstRender);
        }

        private async Task SetGridDefaultSort()
        {
            GridState<object> desiredState = new GridState<object>()
            {
                SortDescriptors = new List<SortDescriptor>()
            {
                new SortDescriptor { Member = nameof(OutageReportDto.RecordedTime), SortDirection = ListSortDirection.Descending }
            }
            };

            await MainGrid.SetState(desiredState);
        }

        private async Task LoadData()
        {
            var result = await outageReportService.GetOutageReportList(LastRequest);

            if (LastRequest.Groups.Count > 0)
            {
                var data = GroupDataHelpers.DeserializeGroups<OutageReportDto>(result.GroupedData);
                OutageReportList = data.Cast<object>().ToList();
            }
            else
            {
                OutageReportList = result.CurrentPageData.Cast<object>().ToList();
            }

            Total = result.TotalItemCount;

            StateHasChanged();
        }

        protected async Task OnReadHandler(GridReadEventArgs args)
        {
            LastRequest = args.Request;
            await LoadData();
        }

    }
}

And my Blazor Page:

@page "/outagereports"

@attribute [Authorize(AvalancheOCPPermissions.OutageReportsGrid.Default)]
@using TVD_Holdings_Ltd.AvalancheOCP.Permissions
@using Microsoft.AspNetCore.Authorization
@using TVD_Holdings_Ltd.AvalancheOCP.Localization
@using Microsoft.Extensions.Localization
@using TVD_Holdings_Ltd.AvalancheOCP.AOR.OutageReports;
@inject IStringLocalizer<AvalancheOCPResource> L
@inject AbpBlazorMessageLocalizerHelper<AvalancheOCPResource> LH
@inherits OutageReportGridBase

<Card>
    <CardHeader>
        <Row Class="justify-content-between">
            <Column ColumnSize="ColumnSize.IsAuto">
                <h2>@L["OutageReportGrid"]</h2>
            </Column>
        </Row>
    </CardHeader>
    <CardBody>
        <TelerikRootComponent>
            <TelerikGrid Data=@OutageReportList @ref="@MainGrid"
                         OnRead="@OnReadHandler" TotalCount=@Total
                         Sortable="true"
                         Groupable="true"
                         FilterMode="@GridFilterMode.FilterMenu"
                         Pageable="true" PageSize="50">
                <GridColumns>
                    <GridColumn Field="@nameof(OutageReportDto.SupplyNetworkName)" FieldType="@(typeof(string))" Title="@L["OutageReportGrid.SupplyNetworkName"]"/>
                    <GridColumn Field="@nameof(OutageReportDto.RecordedTime)" FieldType="@(typeof(DateTime))" Title="@L["OutageReportGrid.RecordedTime"]" DisplayFormat="{0:ddd dd-MMM-yyyy HH:mm}" />
                    <GridColumn Field="@nameof(OutageReportDto.CreationTime)" FieldType="@(typeof(DateTime))" Title="@L["OutageReportGrid.CreationTime"]" DisplayFormat="{0:ddd dd-MMM-yyyy HH:mm}"/>
                    <GridColumn Field="@nameof(OutageReportDto.NotifiedTime)" FieldType="@(typeof(DateTime?))" Title="@L["OutageReportGrid.NotifiedTime"]" DisplayFormat="{0:ddd dd-MMM-yyyy HH:mm}" />
                    <GridColumn Field="Location" FieldType="@(typeof(string))" Title="@L["OutageReportGrid.Location"]" />
                    <GridColumn Field="@nameof(OutageReportDto.Suburb)" FieldType="@(typeof(string))" Title="@L["OutageReportGrid.Suburb"]" />
                    <GridColumn Field="@nameof(OutageReportDto.CauseName)" FieldType="@(typeof(string))" Title="@L["OutageReportGrid.CauseName"]" />
                    <GridColumn Field="@nameof(OutageReportDto.ContactPhoneNumber)" FieldType="@(typeof(string))" Title="@L["OutageReportGrid.ContactPhoneNumber"]" />
                </GridColumns>
            </TelerikGrid>
        </TelerikRootComponent>
    </CardBody>
</Card>

8 Answer(s)
  • User Avatar
    0
    alper created
    Support Team Director

    we are not experienced in Telerik components but will check this. by the way did you find a solution?

  • User Avatar
    0
    mladen.macanovic created

    Based on stackstrace message it seems the problem is with one of your model members. I see that all your Data grid fields are defined with nameof except for Location, eg. <GridColumn Field="Location" FieldType="@(typeof(string))" Title="@L["OutageReportGrid.Location"]" />. Maybe that is the problem? Also can you show us the OutageReportDto class?

    I'm not that familiar with Telerik systems but I remember that Blazor don't work well with generic types when accesed through JSInterop. That is the only thing that comes to my mind regarding the error message. Maybe Telerik is doing something under the hood that could make trouble.

  • User Avatar
    0
    Leonardo.Willrich created

    Hi Alper.

    I haven't found a solution so far. Temporarily, I changed my grid to client-side operations, so, it wlll peform all operation on client side (filter, group, sort).

    Hi mladen.macanovic, I did the test again changing the string for the method nameof, but that haven't changing nothing. I think the problem is when parsing the object DataSourceRequest into Json for the method. The method by itself is not called. I guess it would be easier to be reproduced.

    Here is my class OutageReportDto:

    public class OutageReportDto : AuditedEntityDto<int>, IMultiTenant
        {
            public Guid? TenantId { get; set; }
            public int SupplyNetworkId { get; set; }
            public string SupplyNetworkName { get; set; }
            public DateTime RecordedTime { get; set; }
            public string Location { get; set; }
            
            public string Suburb { get; set; }
            public int CauseId { get; set; }
            public string CauseName { get; set; }
            public string ContactPhoneNumber { get; set; }
            public double LatitudeReporter { get; set; }
            public double LongitudeReporter { get; set; }
            public double LatitudeLocator { get; set; }
            public double LongitudeLocator { get; set; }
            public DateTime? NotifiedTime { get; set; }
            public new DateTime CreationTime { get; set; }
        }
    

    Notice, if I change the grid to Client-Side filter, where I just call a service method with no parameters to return a complete List<OutageReporterDto>, it works fine. So, I don't think that there's an issue with my grid or my Dto class. The problem is only when calling the service method with a parameter type DataSourceRequest and there is a Filter item in this object. If there is no filter items the method works fine.

  • User Avatar
    0
    alper created
    Support Team Director

    I'm adding @Engin the author of this article. maybe he knows it

  • User Avatar
    0
    Leonardo.Willrich created

    Hi,

    Any progress on this item? I am still getting errors. Currently, I'm working in a different project and It will be required using Server side filtering. Even updating the framework to 4.3.0 the issue is still happening.

  • User Avatar
    0
    EngincanV created
    Support Team .NET Developer

    Hi @Leonardo.Willrich, I am checking.

  • User Avatar
    0
    EngincanV created
    Support Team .NET Developer

    Hi again @Leonardo.Willrich, can you change your OnReadHandler method as below?

    protected async Task OnReadHandler(GridReadEventArgs args)
    {
        LastRequest = args.Request;
        List<IFilterDescriptor> filters = null;
        if (LastRequest.Filters.Any())
        {
            var filterDescriptors = LastRequest.Filters.SelectMemberDescriptors().ToList();
            if (filterDescriptors.Any())
            {
                filters = new List<IFilterDescriptor>();
                foreach (var filter in filterDescriptors)
                {
                    filters.Add(new FilterDescriptor { Member = filter.Member, Operator = filter.Operator, Value = filter.Value });
                }
             }
        }
    
        LastRequest.Filters = filters;
        await LoadData();
    }
    
    • FilterDescriptor class has a variable named MemberType and wrong value is assigned this variable automattically.

    If it does not work for you, please let me know. If it works you can close the question.

  • User Avatar
    0
    Leonardo.Willrich created

    That worked like a charm. Good catch. Thank you very much!

Made with ❤️ on ABP v8.2.0-preview Updated on March 25, 2024, 15:11