Result icon indicating copy to clipboard operation
Result copied to clipboard

TranslateResultToActionResult doesn't give PagedInfo

Open khandelwal-arpit opened this issue 4 years ago • 5 comments

While using TranslateResultToActionResult to return responses in the form of ActionResults, there is no way for the caller to know the details of the pagination.

The reason is because the ToActionResult method in the ResultExtensions class just cares about the "result.GetValue()" and ignores the Pagination details which were fed by the upstream services as shown below:

return Result<IEnumerable<StoryDto>>.Success(pagedStoryDtos).ToPagedResult(pagedInfo);

Is there a way we can modify this behavior so that the caller can see the pagination details too?

khandelwal-arpit avatar Aug 09 '21 07:08 khandelwal-arpit

Good question; will need to review.

ardalis avatar Aug 10 '21 14:08 ardalis

Hello Steve,

I am facing the same issue. Any news about the development of this feature ?

Aurélien

aurelienhayet avatar Mar 24 '24 23:03 aurelienhayet

This may not be the most elegant solution but given you have a StoryDto, could you create a PagedStoryDto (or whatever name) and use the Map method to combine StoryDto + PagedInfo into a single object that would get returned. Something like this:

return Result<IEnumerable<StoryDto>>.Success(pagedStoryDtos)
   .ToPagedResult(pagedInfo)
   .Map(r => new PagedStoryDto
   {
     StoryProperty = r.StoryProperty,
     PageNumber = r.PagedInfo.PageNumber,
     ... // other properties
   };

We might be able to add an overload of Map for paged results that does this out of the box in a future update. 🤔

KyleMcMaster avatar Mar 27 '24 16:03 KyleMcMaster

I did something a little bit more complex : I have overriden the TranslateResultToActionResultAttribute to deal with the paged results.

public class AdvancedTranslateResultToActionResultAttribute : TranslateResultToActionResultAttribute
{
    public override void OnActionExecuted(ActionExecutedContext context)
    {
        if ((context.Result as ObjectResult)?.Value is not IResult result) return;

        if (context.Controller is not ControllerBase controller) return;

        base.OnActionExecuted(context);

        if (result.Status is not ResultStatus.Ok) return;

        var type = result.GetType();
        var ardalisPagedResultType = typeof(PagedResult<>);
        if (type.Namespace != ardalisPagedResultType.Namespace
            || type.Name != ardalisPagedResultType.Name)
        {
            return;
        }

        if (!result.ValueType.IsAssignableTo(typeof(IEnumerable<object>))) return;

        var pagedInfoProperty = type.GetProperty(nameof(PagedInfo));

        if (pagedInfoProperty?.GetValue(result) is not PagedInfo pagedInfoValue) return;

        var pagedResultType = typeof(Contracts.Core.PagedResult<>);

        var itemType = result.ValueType.GetGenericArguments()[0];

        var constructed = pagedResultType.MakeGenericType(itemType);

        var pagedInfo = Contracts.Core.PagedInfo.FromArdalisPagedInfo(pagedInfoValue);

        context.Result = controller.StatusCode(
                    context.HttpContext.Response.StatusCode,
                    Activator.CreateInstance(constructed, pagedInfo, result.GetValue()));
    }
}

As you can see, I use reflection to Map Ardalis PagedResult and PagedInfo to my own equivalent classes Contracts.Core.Paged*

With this approach, I obtain a generic solution for all my controllers and paged models.

aurelienhayet avatar Mar 27 '24 16:03 aurelienhayet