Renaming "Areas" folder in ASP.NET MVC - asp.net-mvc

Is it possible to rename Areas folder? Will visual studio and razor find correct path to views in this case ?

Yes it's possible! Let's make things easy focusing on the task of renaming an area from Foo to Bar.
Rename the area folder Foo to Bar
The area folder must contain a file which name ends in AreaRegistration.cs. Rename it from FooAreaRegistration.cs to BarAreaRegistration.cs. (current step is not mandatory but helps to keep your code clean)
Edit the file BarAreaRegistration.cs from:
public override string AreaName
{
get
{
return "Foo";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Foo_default",
"Foo/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
to:
public override string AreaName
{
get
{
return "Bar";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Bar_default",
"Bar/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
Rename also the namespace of the class from namespace xxxxx.Foo to xxxxx.Bar
Replace the namespace for every controller inside the Bar folder
Replace Foo with Bar in Views when needed (usually you get an absolute path when using specific area layout
Tested MVC > 4.0
Hope this helps

Yes it is possible to rename Areas folder in MVC. This is the location where Razor View Engine looks for Area Views. Add the following code in the Global.asax file. Now View Engine will search Area Views in the location ~/Modules/HelloArea/Views/Hello/Index.cshtml instead of ~/Areas/HelloArea/Views/Hello/Index.cshtml
RazorViewEngine viewEngine = new RazorViewEngine();
viewEngine.AreaMasterLocationFormats =
viewEngine.AreaPartialViewLocationFormats =
viewEngine.AreaViewLocationFormats =
new string[] {
"~/Modules/{2}/Views/{1}/{0}.cshtml",
"~/Modules/{2}/Views/Shared/{1}/{0}.cshtml"};
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(viewEngine);

Related

Using MVC 4 areas at custom path

I'm trying to use areas at a custom path, and I'm having issues. I've been googeling a bunch, but havent found a solution.
My project is a EPiServer CMS project (which shouldn't have any effect I think, just wanna mention it, in case it does)
My structure is
Root
CompanyName
Areas
Commerce
Controllers
Models
Views
Cms
Controllers
HomePageController
Models
Views
HomePage
Index.cshtml
So I have a layer more to the tree then 'normal' which is the 'CompanyName'
I have this in global.asax.cs
protected void Application_Start()
{
ViewEngines.Engines.Add(new AreaTemplateViewEngineDynamic());
AreaRegistration.RegisterAllAreas();
...
}
I have a Custom RazorEngine (Could have just added more paths to the default, but have this solution as of now)
public class AreaTemplateViewEngineDynamic : RazorViewEngine
{
public AreaTemplateViewEngineDynamic()
{
this.PartialViewLocationFormats = this.ViewLocationFormats = this.MasterLocationFormats =
new string[]
{
"~/CompanyName/Views/{1}/{0}.cshtml", "~/CompanyName/Views/Shared/{0}.cshtml"
};
this.AreaMasterLocationFormats = this.AreaPartialViewLocationFormats = this.AreaViewLocationFormats =
new string[]
{
"~/CompanyName/Areas/{2}/Views/{1}/{0}.cshtml", "~/CompanyName/Areas/{2}/Views/Shared/{0}.cshtml"
};
}
}
Adding this area registration
public class CmsAreaRegistration: AreaRegistration
{
public override string AreaName
{
get { return "Commerce"; }
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Cms_default",
"Cms/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "Root.CompanyName.Areas.Cms.Controllers" }
);
}
}
When I try to load the page, it seems it doesnt look at the Area paths, only the non-area paths.
The view 'index' or its master was not found or no view engine supports the searched locations. The following locations were searched:
~/Views/HomePage/index.aspx
~/Views/HomePage/index.ascx
~/Views/Shared/index.aspx
~/Views/Shared/index.ascx
~/Views/HomePage/index.cshtml
~/Views/HomePage/index.vbhtml
~/Views/Shared/index.cshtml
~/Views/Shared/index.vbhtml
~/CompanyName/Views/HomePage/index.cshtml
~/CompanyName/Views/Shared/index.cshtml
The path I want it to find is
~/CompanyName/Areas/Cms/Views/HomePage/index.cshtml
Also if I had to use
#{Html.RenderAction("MiniCart", "Cart", new { area = "Commerce"} );}
I would expect it to finde
~/CompanyName/Areas/Commerce/Views/Cart/MiniCart.cshtml
You are only setting the location for the AreaMasterLocation when you should also set the following locations:
AreaPartialViewLocationFormats
AreaViewLocationFormats
Find the following class in the object browser: VirtualPathProviderViewEngine for more properties and methods.
I wrote my own RazorViewEngine, where I added some custom codes to finding paths.
Could just use the URL, because the URL was controlled by the CMS, so the URL didnt represent the MVC path.

Issue with views using Areas

I am about to pull my hair out!!
So I have areas setup in my MVC 3 project, I have an AccountController and Model as well as a view
When you add an area VS sets up the structure for you and I have not modified that. With all of that said, my views are not working. The controller works, when I set a breakpoint I see
// **************************************
// URL: /Account/LogOn
// **************************************
public ActionResult LogOn()
{
return View();
}
It is getting hit however I get the YPOD(Yellow page of Death) when returning the view. I have tried a view with partial rendering as well as the LogOn.cshtml, partial being located in the shared folder for the area and the non partial in the Views/Account folder.
Is this just broken or am I missing something?
I was missing a controller="Account" in my area registration. It now works:
public class AccountAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "Account";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Account_default",
"Account/{controller}/{action}/{id}",
new { area = "Account", controller = "Account", action = "Index", id = UrlParameter.Optional }
);
}
}

MVC 3 - New Area - 404 error - Resource not found - have tried route debugger

I have a small MVC 3 app - bit of a demo ground.
I have one area and thats been working fine.
I have just added another area expecting to just spin up the app and it work - but no, 404 - The resource cannot be found.
The map route in the AreaRegistration is the default (as is the first area i created).
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Postcard_default",
"Postcard/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
I have tried adding in a specific controller into this, but nothing.
So I downloaded Phil Haack's RouteDebugger and my route is found when typing in http://server/Postcard/Create (which is where I am trying to get too)
Structure of the Area
My controller
public class CreateController : Controller
{
private ILogger Logger { get; set; }
private ICardSender Emailer { get; set; }
private IOCCardRepository CardRepository { get; set; }
public CreateController(ILogger logger, ICardSender cardSender, IOCCardRepository repository)
{
this.Logger = logger;
this.Emailer = cardSender;
this.CardRepository = repository;
}
//
// GET: /Postcard/Create/
public ActionResult Index()
{
var model = new OCPostcardModel().Create();
return View(model);
}
NOW: I have since deleted the entire area, tried again it didn't work. So I added in the specific controller in the route (Inside AreaRegistration file)
context.MapRoute(
"Postcard_default",
"Postcard/{controller}/{action}/{id}",
new { controller = "Create", action = "Index", id = UrlParameter.Optional }
);
And its working...I don't know why it didn't work when I did this before, but it is now.
Still curious though as I've not seen anyone add in this controller into route in any of the demo's i've looked at - and I haven't got it in my other area?
I ran into this when I moved a controller into an Area but forgot to update the namespace. The controller name is scoped to the Area's namespace. So "Some" in "Area" will map to App.Areas.Area.Controllers.SomeController, which didn't exist.
You were missing the controller part in your maproute
Try to add a class PostCardAreaRegistration under PostCard Area
using System.Web.Mvc;
namespace Areas.PostCard
{
public class PostCardAreaRegistration: AreaRegistration
{
public override string AreaName
{
get
{
return "PostCard";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"PostCard_default",
"PostCard/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
}
}

asp.net mvc 2 preview 2 and Spark

Some body tried Spark View Engine with asp.net mvc 2 preview 2?
I have a problem with AREAS.
It looks likes spark engine looks **.spark* files inside of Views folders only instead of Areas folder in additionally.
My question is:
Somebody has information how to add it?
Spark will not automatically check the area views location in the current version. If you're willing to change the source (which i assume you are if you're doing mvc 2 stuff), here's the fix:
You have to modify the file src\Spark.Web.Mvc2\Descriptors\AreaDescriptorFilter.cs so that it reads as below (changes highlighted by **):
Note: I don't have the machine i did this on with me, so the slashes in the format string MIGHT need to be forward slashes.
Also, it is possible to create this class in your own code and pass it in when you register the view engine, but I don't remember the configuraiton code off the top of my head.
That's the approach I did since I wanted to modify the spark source as little as possible.
public class AreaDescriptorFilter : DescriptorFilterBase
{
**private const string areaPathFormatString = "~\\Areas\\{0}\\Views";**
public override void ExtraParameters(ControllerContext context, IDictionary<string, object> extra)
{
object value;
if (context.RouteData.Values.TryGetValue("area", out value))
extra["area"] = value;
}
public override IEnumerable<string> PotentialLocations(IEnumerable<string> locations, IDictionary<string, object> extra)
{
string areaName;
return TryGetString(extra, "area", out areaName)
**? locations.Select(x => Path.Combine(string.Format(areaPathFormatString,areaName), x)).Concat(locations)**
: locations;
}
}
Spark looks for a constraint or default value key "area" in a route to determine the view location. MVC 2 area support does not add this by default, you have to do it when declaring your area:
public class AdminRoutes : AreaRegistration
{
public override string AreaName
{
get { return "admin"; }
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"admin_default",
"Admin/{controller}/{action}/{id}",
new { controller = "Dashboard", action = "Index", id = "", area = "admin" },
new [] { "MyProject.Areas.Admin.Controllers" });
}
}
Note the area = "admin" inside the defaults object.

MVC 2 AreaRegistration Routes Order

I noticed that in MVC 2 Preview 2, AreaRegistration is loading the routes for each area in an arbitrary order. Is there a good way to get one before the other?
For example, I have two areas - "Site" and "Admin". Both have a "Blog" controller.
I would like the following:
/admin/ --> go to Admin's Blog controller
/ --> go to Site's Blog controller.
The problem is that it is loading the site's route first, so it is matching {controller}/{action}/{id} instead of admin/{controller}/{action}/{id} when I go to the url "/admin/". I then get a 404, because there is no Admin controller in the "Site" area.
Both areas default to the "Blog" controller. I realize I could simply put site/{controller}/... as the url, but I would rather have it at the root if possible. I also tried keeping the default route in the global RegisterRoutes function, however, it is then not sent to the "Sites" area.
Thanks in advance!
Aside from what Haacked said, it is very much possible to order area registrations (and thus their routes). All you have to do is register each area manually, in whatever order you want. It's not as sleek as calling RegisterAllAreas() but it's definitely doable.
protected void Application_Start() {
var area1reg = new Area1AreaRegistration();
var area1context = new AreaRegistrationContext(area1reg.AreaName, RouteTable.Routes);
area1reg.RegisterArea(area1context);
var area2reg = new Area2AreaRegistration();
var area2context = new AreaRegistrationContext(area2reg.AreaName, RouteTable.Routes);
area2reg.RegisterArea(area2context);
var area3reg = new Area3AreaRegistration();
var area3context = new AreaRegistrationContext(area3reg.AreaName, RouteTable.Routes);
area3reg.RegisterArea(area3context);
}
Another option is to take the code for RegisterAllAreas(), copy it into your own app, and build your own mechanism for determining the order. It is quite a bit of code to copy if you want all the fancy caching logic that the built-in method does, but your app might not even need that.
Currently it's not possible to order areas. However, I think it makes sense to try and make each area as independent from other areas as possible so the order doesn't matter.
For example, instead of having the default {controller}/{action}/{id} route, maybe replace that with specific routes for each controller. Or add a constraint to that default route.
We are mulling over options to allow ordering, but we don't want to overcomplicate the feature.
I make this solution:
AreaUtils.cs
using System;
using System.Web.Mvc;
using System.Web.Routing;
namespace SledgeHammer.Mvc.Site
{
public static class Utils
{
public static void RegisterArea<T>(RouteCollection routes,
object state) where T : AreaRegistration
{
AreaRegistration registration =
(AreaRegistration)Activator.CreateInstance(typeof(T));
AreaRegistrationContext context =
new AreaRegistrationContext(registration.AreaName, routes, state);
string tNamespace = registration.GetType().Namespace;
if (tNamespace != null)
{
context.Namespaces.Add(tNamespace + ".*");
}
registration.RegisterArea(context);
}
}
}
In global.asax:
Utils.RegisterArea<SystemAreaRegistration>(RouteTable.Routes, null);
Utils.RegisterArea<ClientSitesAreaRegistration>(RouteTable.Routes, null);
//AreaRegistration.RegisterAllAreas(); do not dublicate register areas
No requred changes to generated area registration code.
I also use custom constrant in routes to filter routes by type of domain in request (system domain or user site).
This is my area registrations as example:
namespace SledgeHammer.MVC.Site.Areas.System
{
public class SystemAreaRegistration : AreaRegistration
{
public override string AreaName
{
get { return "System"; }
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"System_Feedback",
"Feedback",
new { controller = "Feedback", action = "Index" }
);
context.MapRoute(
"System_Information",
"Information/{action}/{id}",
new { controller = "Information", action = "Index", id = UrlParameter.Optional }
);
}
}
}
namespace SledgeHammer.MVC.Site.Areas.ClientSites
{
public class ClientSitesAreaRegistration : AreaRegistration
{
public override string AreaName
{
get { return "ClientSites"; }
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"ClientSites_default",
"{controller}/{action}/{id}",
new { controller = "Site", action = "Index", id = UrlParameter.Optional },
new { Host = new SiteInGroups("clients") }
);
}
}
}
For reference,
In MVC3 (don't know about MVC2) when you just want to map root to a specific area/controller you could simply use a global route.
Just remember to specify the namespace/area.
routes.MapRoute(
"CatchRoot", "",
new { controller = "SITEBLOG-CONTROLLER-NAME", action = "Index"}
).DataTokens.Add("area", "SITE-AREA-NAME");

Resources