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

SmartBreadcrumbsException: The full name Web.CreateModel doesn't contain 'Pages' #56

Closed ciaran036 closed 4 years ago

ciaran036 commented 4 years ago

I'm using v3.1.1 of SmartBreadcrumbs.

Getting this error message:

SmartBreadcrumbsException: The full name Web.CreateModel doesn't contain 'Pages'. SmartBreadcrumbs.Extensions.ReflectionExtensions.ExtractRazorPageKey(Type pageType)

The error gets thrown when the Startup code runs for adding breadcrumbs:

services.AddBreadcrumbs(GetType().Assembly, options =>
{
    options.OlClasses = "breadcrumb hidden-print";
});

I have a Razor page model which inherits an abstract type for implementing a user creation wizard:

    [Breadcrumb("User")]
    public class CreateModel : WizardPageModel<UserWizardTransaction, IUserProcessor>
    {
        public CreateModel(IWizardService<UserWizardTransaction> wizardService, IUserProcessor processor) 
            : base(wizardService, processor)
        {

        }

        public override ActionResult Redirect(int? entityId)
        {
            return RedirectToAction("Detail", new { id = entityId })
                .SuccessToast($"User {MessageHandler.CreationSuccess}");
        }
    }

An excerpt from my abstract Razor Page model which exists in my Web.Models namespace:

    public abstract class WizardPageModel<TWizardTransaction, TProcessor> : PageModel
        where TWizardTransaction : WizardTransaction
        where TProcessor : ITransactionProcessor
    {
        [BindProperty]
        public TWizardTransaction WizardTransaction { get; set; }

        public TProcessor Processor { get; set; }

        protected IWizardService<TWizardTransaction> WizardService { get; set; }

        public abstract ActionResult Redirect(int? entityId);

        protected WizardPageModel(IWizardService<TWizardTransaction> wizardService, TProcessor processor)
        {
            WizardService = wizardService;
            Processor = processor;
        }

        public IActionResult OnGet(Guid? transactionId, int? step)
        {
            WizardTransaction = WizardService.GetTransactions(transactionId, step);
            return Page();
        }

        public IActionResult OnPost()
        {
            var action = WizardTransaction.IsFinalStep() ? WizardAction.Finish : WizardAction.Next;
            return Save(action);
        }

        public IActionResult OnPostSave()
        {
            return Save(WizardAction.Save);
        }
    }

Any thoughts on why this might be throwing an exception? On a normal Razor page, adding the breadcrumb attribute works correctly.

e.g.

   [Breadcrumb("Settings")]
    public class IndexModel : PageModel
    {
        private readonly DataContext _dataContext;

        public SystemSettings SystemSettings { get; set; }

        public IndexModel(DataContext dataContext)
        {
            _dataContext = dataContext;
        }

        public void OnGet()
        {
            SystemSettings = _dataContext.Settings.Result;
        }

        public IActionResult OnPost()
        {
            if (ModelState.IsValid)
            {
                return RedirectToPage("Index").SuccessToast("Settings updated");
            }

            return Page();
        }
    }

Therefore, I'd have to assume the problem is something to do with my user create wizard inheriting the abstract WizardPageModel type?

Error stack trace:

SmartBreadcrumbs.SmartBreadcrumbsException: The full name Web.CreateModel doesn't contain 'Pages'. at SmartBreadcrumbs.Extensions.ReflectionExtensions.ExtractRazorPageKey(Type pageType) at SmartBreadcrumbs.BreadcrumbManager.HasBreadcrumb(Type type, BreadcrumbNodeEntry& entry) at SmartBreadcrumbs.BreadcrumbManager.Initialize(Assembly assembly) at SmartBreadcrumbs.Extensions.ServiceCollectionExtensions.AddBreadcrumbs(IServiceCollection services, Assembly assembly, BreadcrumbOptions options) at SmartBreadcrumbs.Extensions.ServiceCollectionExtensions.AddBreadcrumbs(IServiceCollection services, Assembly assembly, Action`1 optionsSetter) at Web.Startup.ConfigureServices(IServiceCollection services) in C:\Git\EcomFrameworkCore\Web\Startup.cs:line 114 at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.InvokeCore(Object instance, IServiceCollection services) at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.<>cDisplayClass9_0.gStartup|0(IServiceCollection serviceCollection) at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.Invoke(Object instance, IServiceCollection services) at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.<>cDisplayClass8_0.b0(IServiceCollection services) at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.UseStartup(Type startupType, HostBuilderContext context, IServiceCollection services) at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.<>c__DisplayClass12_0.b__0(HostBuilderContext context, IServiceCollection services) at Microsoft.Extensions.Hosting.HostBuilder.CreateServiceProvider() at Microsoft.Extensions.Hosting.HostBuilder.Build() at Web.Program.Main(String[] args) in C:\Git\EcomFrameworkCore\Web\Program.cs:line 11

Thanks!

zHaytam commented 4 years ago

The error happens because the full name of the type Web.CreateModel doesn't contain the keyword Pages. SmartBreadcrumbs assumes that all Razor Pages are available in a folder named Pages.

Mind telling me why you changed their place? To see whether to remove this "requirement".

ciaran036 commented 4 years ago

Hi, my files were in the wrong location but my namespaces were at the root 'Web' instead of 'Web.Pages.User' for example. My bad. Adjusting the namespace causes it to work correctly for any page.

It seems that when creating a Razor Page in Visual Studio the namespace defaults to the root instead of 'Web.Pages.MyType'. Does that happen for you? I noticed this previously and thought it was strange but never questioned it further.

Thanks for the help, much appreciated!

zHaytam commented 4 years ago

It personally never happened to me, that's weird. I'm glad I was able to help!

ciaran036 commented 4 years ago

There was a bug in Visual Studio. Namespaces for Razor pages are now correct after updating Visual Studio 2019.