2

I should create a website with multiple sections. The functionalities and views for these sections are exactly the same but I want different URL such as

  • //localhost:111/Works/Index
  • //localhost:111/OldJobs/Index

For this reason, I created a BaseReferenceController with all ActionResult I need. For example:

public class BaseReferenceController : Controller
{
    public virtual ActionResult Index(int? sectionId)
    {
        Articles articles = GetArticles(sectionId);
        // more code base on sectionId
        return View(articles);
    }

    // more ActionResult
}

Now I create a new controller to have a new URL Works like

public class WorksController : BaseReferenceController
{
    public override ActionResult Index(int? sectionId)
    {
        return base.Index(2);
    }
}

An error occurs

The view 'Index' or its master was not found or no view engine
supports the searched locations. The following locations were
searched: 
~/Views/Works/Index.aspx 
~/Views/Works/Index.ascx
~/Views/Works/Index.aspx 
~/Views/Shared/Index.ascx
~/Views/Works/Index.cshtml 
~/Views/Works/Index.vbhtml
~/Views/Shared/Index.cshtml 
~/Views/Shared/Index.vbhtml

My intention was that BaseReferenceController creates the page and WorksController returns the ActionResult comes from BaseReferenceController.

I tried to use RedirectToActionPermanent but I can't have the result I want.

Update Forget breadcrumbs.

Update/2

Based on @ironstone13 answer (thanks!) I tried to create a new route:

routes.MapRoute(
    "Works",
    "Works/{action}/{id}/{slug}",
    new { controller = "BaseReferenceController", action = "Index", 
          sectionId = 2, slug = UrlParameter.Optional },
    new { id = @"\d+" });

Where action is the action in my Controller, id is the identifier of a record to show or edit (if it is necessary), slug is slug :) but I need a room to pass the sectionId (because without this I don't know how to filter result).

I received a message in Insight debug

The controller for path '/Works' was not found or does not implement IController.

and in Visual Studio I receive this

System.InvalidOperationException occurred HResult=0x80131509
Message=The constraint entry 'slug' on the route with route template 'Works/{action}/{sectionId}/{slug}' must have a string value or be of a type which implements 'System.Web.Routing.IRouteConstraint'.
Source=System.Web.Mvc

Where am I wrong? After that I can call

  • //localohost:111/Works/Index
  • //localohost:111/Works/New/2
  • //localohost:111/Works/View/23/Document-for-client

? Thanks

Enrico
  • 3,592
  • 6
  • 45
  • 102

2 Answers2

3

One option would be to use custom Action Filter and place your breadcrumb logic there, similar to what was done here:How would you implement a breadcrumb helper in asp.net mvc?

Arguably, breadcrumbs is not the core functionality of your controller methods, so it makes sense to use AOP in the form of custom action filers applied by using attributes, and such infrastructural things should not be the main factor that defines your class hierarchy.

Your current code does not work because index view is searched by convention using invoking controller name, and, obviously, you don't have a view for base controller, and you most probably won't - I assume you need a separate view for each of your entities (Works and OldJobs) - so how will you point to the proper view?.

It is best to avoid passing path to the view altogether and just don't call the View() from the base class.

If the views and controllers for Works and OldJobs are completely the same - it makes sense to have only one view and one controller, and use routing to map different urls to one controller action like below

public class MvcApplication : HttpApplication
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.MapRoute(
            "Works",
            "Index",
            new { controller = "BaseReferenceController", action = "Index" });

        routes.MapRoute(
            "OldJobs",
            "Index",
            new { controller = "BaseReferenceController", action = "Index" });

Update : if you need to pass multiple parameters in the URI consider defining a separate type for your parameters and using parameter binding from uri, see parameter binding from uri. A similar approach is described here Routing with Multiple Parameters using ASP.NET MVC

Your uri could look something like this: //localohost:111/Works/View/?id=23&slug=Document-for-client&sectionId=2. For multiple parameters using FromUri parameter binding for GET methods, and placing all of the parameters in the QueryString portion of the URI is much cleaner if you don't have some kind of logical hierarchy in your parameters.

If you don't put the parameters in the query string then your URI becomes ambiguous. What does //localohost:111/Works/View/1/2 mean? Is Id=1 and SectionId=2 or vice versa?

Community
  • 1
  • 1
ironstone13
  • 3,325
  • 18
  • 24
  • Thanks but the problem is not the breadcrumb but all content in views. – Enrico May 06 '17 at 13:09
  • @Enrico, this looks like a use case for routing then - let me look it up – ironstone13 May 06 '17 at 13:11
  • @Enrico, what is slug? Why is `sectionId` not in the route, and hard-coded? Should it be dynamic, and come from URL? Is `sectionId` the same as `id`? Also, can you say which URL works and which does not? – ironstone13 May 06 '17 at 15:47
  • `slug` is the title of a document i.e. `//localhost:44889/Works/View/19/Amazon-documents`. I changed `id` , my mistake. – Enrico May 06 '17 at 15:53
  • No one is working. If I try the basic `//localhost:44889/Works` doesn't work. – Enrico May 06 '17 at 15:54
  • sorry, `sectionId` is the code I have to pass to the `BaseReferenceController` to filter records. – Enrico May 06 '17 at 15:56
  • `id` is the reference identification, the id in the database. I pass this id when I want to show or edit it. – Enrico May 06 '17 at 15:57
  • @Enrico, I have to admit I don't entirely understand your requirements now. I've added some of the details to my answer. Please reach out if you need more info. Cheers – ironstone13 May 06 '17 at 18:46
  • Thanks for your replies – Enrico May 07 '17 at 09:00
0

MVC convention is that all views use ~/Views/ControllerName/Action for the view pages by default. If you want it to use a specific view page, you need to specify it in the base controller.

public class BaseReferenceController : Controller
{
    public virtual ActionResult Index(int? sectionId)
    {
        ViewBag.Bread = BreadcrumbHelpers.GetBreadcrumb((int)sectionId);
        // more code base on sectionId
        return View("~/Views/FolderWhereYourViewIs/View.cshtml");
    }

    // more ActionResult
}
  • All views are under `BaseReferenceController` folder. I don't want to create another `Views` , I want to use only `BaseReference` controller and views so I change what I want only in one place. Other controllers are only for a different url. – Enrico May 06 '17 at 13:08
  • So replace "FolderWhereYourViewIs" with the folder to your BaseReference view, and "View.cshtml" with your "Index.cshtml". – Anthony Michelizzi May 06 '17 at 13:13
  • There is no sense to have a base controller and two inherited controllers that all do the same thing! Just use routing for this. – ironstone13 May 06 '17 at 13:25