zHaytam / SmartBreadcrumbs

A utility library for ASP.NET Core (both MVC and Razor Pages) websites to easily add and customize breadcrumbs.
https://blog.zhaytam.com/2018/06/24/asp-net-core-using-smartbreadcrumbs/
MIT License
161 stars 77 forks source link

ActionResult actions aren't considered #38

Closed Renato-Garbim closed 4 years ago

Renato-Garbim commented 5 years ago

` public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { var child = await output.GetChildContentAsync();

        string nodeKey = GetNodeKey(ViewContext.ActionDescriptor.RouteValues);

        var node = ViewContext.ViewData["BreadcrumbNode"] as BreadcrumbNode ?? _breadcrumbManager.GetNode(nodeKey);

        output.TagName = BreadcrumbManager.Options.TagName;

        // Tag Classes
        if (!string.IsNullOrEmpty(BreadcrumbManager.Options.TagClasses))
        {
            output.Attributes.Add("class", BreadcrumbManager.Options.TagClasses);
        }

        output.Content.AppendHtml($"<ol class=\"{BreadcrumbManager.Options.OlClasses}\">");

        var sb = new StringBuilder();

        // Go down the hierarchy
        if (node != null)
        {
            if (node.OverwriteTitleOnExactMatch && node.Title.StartsWith("ViewData."))
                node.Title = ExtractTitle(node.OriginalTitle);

            sb.Insert(0, GetLi(node, node.GetUrl(_urlHelper), true));

            while (node.Parent != null)
            {
                node = node.Parent;

                // Separator
                if (BreadcrumbManager.Options.HasSeparatorElement)
                {
                    sb.Insert(0, BreadcrumbManager.Options.SeparatorElement);
                }

                sb.Insert(0, GetLi(node, node.GetUrl(_urlHelper), false));
            }
        }

        // If the node was custom and it had no defaultnode
        if (!BreadcrumbManager.Options.DontLookForDefaultNode && node != _breadcrumbManager.DefaultNode)
        {
            // Separator
            if (BreadcrumbManager.Options.HasSeparatorElement)
            {
                sb.Insert(0, BreadcrumbManager.Options.SeparatorElement);
            }

            sb.Insert(0, GetLi(_breadcrumbManager.DefaultNode,
                _breadcrumbManager.DefaultNode.GetUrl(_urlHelper),
                false));
        }

        output.Content.AppendHtml(sb.ToString());
        output.Content.AppendHtml(child);
        output.Content.AppendHtml("</ol>");
    }`

At line 47 in the var node, when the action is an async he will always return a null, still tryng to find why, but probably some await missing.

zHaytam commented 5 years ago

Can you show me your action that it fails to get?

Renato-Garbim commented 5 years ago

This

    [Breadcrumb("My Action")]
    public async Task<ActionResult> Index()
    {
        var model = new TokenModelList
        {
            PaginacaoEOrdenacao = (ModelPaginacaoEOrdenacao)TempData["PaginacaoEOrdenacao"],
            Pesquisa = (TokenPesquisaDTO)TempData["Pesquisa"]
        };

        TempData["PaginacaoEOrdenacao"] = null;
        TempData["Pesquisa"] = null;
        model = await CarregarViewDataAsync(model);

        return View(model);
    }
zHaytam commented 5 years ago

The library handles async actions. I'm not sure where the problem is. Did you try and breakpoint that line and see what nodeKey is?

Renato-Garbim commented 5 years ago

Sorry , how could i debug to see the nodeKey ? What i found out till now is, when i got an Action that is async Task< IActionResult > it works, but if i replace the IActionResult for ActionResult i did not work.

Renato-Garbim commented 5 years ago
    [Breadcrumb("Produto")]
    public async Task<ActionResult> Index()
    {
        return View();
    }

returns this:

Breadcrumb1

that doesnt work as it should. And my Action like this

    [Breadcrumb("Produto")]
    public async Task<IActionResult> Index()
    {
        return View();
    }

returns

Breadcrumb2

this one works, he get the nodeKey if this is what you mean..

Renato-Garbim commented 5 years ago

At ReflectionExtensions class i add a custom line to identify this sort of situation as well

    #region Fields

    private static readonly Type PageModelType = typeof(PageModel);
    private static readonly Type ControllerType = typeof(Controller);
    private static readonly Type ActionResultType = typeof(IActionResult);
    private static readonly Type ActionResultTaskType = typeof(Task<IActionResult>);

    //Custom Code
    private static readonly Type ActionResultClassType = typeof(Task<ActionResult>);

and it seems to be working just fine, but i dont get very well all the code to point out what kind of troube this can cause at the future.

Do you got noted at somewhere why did you make only for the interface of ActionResult ? with some quick search I found this https://forums.asp.net/t/2071936.aspx?What+is+difference+between+Actionresult+and+IActionresult where i get that IActionResult has a broader coverage of cases, but in another ones i prefer to use something more strongly typed like ActionResult. This makes some sense ?

zHaytam commented 5 years ago

Imho, there is no real difference between returning IActionResult or ActionResult. Although I prefer the interface. If ActionResult inherits from IActionResult then I can make sure SmartBreadcrumbs works with every return type that inherits it. For now, you can work with your work-around until I update the library :')