Should I be calling each of my MVC views Index.aspx? - asp.net-mvc

If I'm not mistaken - the conventions of ASP.NET MVC seem to want me to do the following for a controller view.
Thats to day I create 'Products' directory into which I place my 'Index' view. I then create a 'ProductsController' and create an 'Index' method on it which returns a View. Returning just View() with no arguments will go and fetch the 'Index.aspx' page because its the same name as the method.
public class ProductsController : Controller
{
public ActionResult Index()
{
return View(); // looks for Index.aspx in Products directory
}
}
Now thats just fine. BUT I'll end up with a billion Index.aspx pages, and I'm one of these people that never closes any files so I'll end up going crazy.
Alternatively I can create Products/Products.aspx and change my controller to the following :
public class ProductsController : Controller
{
public ActionResult Index() // my default routing goes to Index (from sample project)
{
return View("Products");
}
}
I understand how that works, and that its completely fine within the MVC design pattern to do this. Its not a hack or anything like that.
My problem (after listening to this PDC video) is that convention over customizabiltity is favored in MVC (or whatever the correct phrase is).
So I'm wondering if I'm missing a third way, or if people are just fine with 50 Index tabs in Visual Studio?

Having the default action for each controller have the same name just simplifies the routing (check global.asax). Also, it all things (Products, Books, Contacts, ...) use actions/views with the same name for the same function, then the code becomes much easier to navigate and understand. This use of convention is especially important when working as part of a team as it will encourage consistent code across developers.
While looking at another question, I ran across SimplyRestfulRouting in the MVCContrib project on codeplex. This might give you some ideas.

I think you should name the method after the action and name the view (if it makes sense and it's not shared between actions, the same as the action). You should probably change your routing mechanism as Index isn't really a descriptive name. The action name should represent what it does (just like any method) and shouldn't be hardcoded to Index or something like that. Routing should be edited instead.

It's just a pattern. Use the pattern, but make it work for you. Personally, I like to have my Views named by their function and I don't have many Index.aspx pages, because I don't have many indices.

Related

Getting 404 on url <site>/auction/{Some Guid}

A bit new to Umbraco, so this might be a bit of a scattered question.
I'm using 5.1.
I have a document type called Auction with a selected Template called Auction Details
My end goal is to call this controller method on my AuctionSurfaceController
[ChildActionOnly]
public PartialViewResult Detail(string id)
{
Guid auctionId;
if (Guid.TryParse(id, out auctionId))
{
var auction = auctionService.Client.GetAuction(auctionId);
return PartialView(auction);
}
return null;
}
As of this moment when I go to /Auction - it hits this method and passes in "Auction" into the method, when I go to /Auction/{GUID} i just get a 404
Could I please get some general guidance - or requests for clarifications on how to accomplish this. I would very much appreciate it
Cheers!
It sounds like the routing is working properly.
Assuming that you're executing from the context of being on an 'auction detail' page, it would then make sense that /{GUID} would serve as the id parameter. (rather than www.mysite.com/auctions/auction/id)
Sometimes this question comes up because there's more than one form on a page, and it's difficult to figure out how umbraco will know which controller Umbraco will post to. This is where the bind attribute comes into play.
However, if you want to use custom routing, because Umbraco 5 is built on MVC, you can always create your own areas and controllers.

T4MVC not picking up the Views in Areas (ASP.NET MVC 3 RTM)

T4MVC is working fine everywhere except in Areas.
In Areas, it picks up the Controllers and the Actions, but not the Views.
So, I cannot write, in my controller:
return View(MVC.MyArea.MyController.Views.MyView);
Outside of the Areas, I CAN write:
return View(Views.MyOtherView);
I can also refer to actions in my area controllers:
MVC.MyArea.MyController.MyAction()
In other words:
a. I can get anything I want, if it is not in an Area.
b. I can get at the Actions in my Area controllers.
c. But I CANNOT get my Views in my Area.
What could the problem be?
TIA
EDIT:
The issue is getting T4MVC to rerun (see David Ebbo's answer and my "answer").
What you have should work, and I verified it by:
Creating a new MVC3 app and adding the latest T4MVC 2.6.40 (via nuget)
Adding an Area named MyArea
Adding a controller named MyController
Adding a view named MyView
Make sure you rerun the T4MVC.tt custom tool
After that, I'm able to write:
namespace Mvc3Application.Areas.MyArea.Controllers {
public partial class MyController : Controller {
public virtual ActionResult Index() {
return View(Views.MyView);
}
}
}
or
namespace Mvc3Application.Areas.MyArea.Controllers {
public partial class MyController : Controller {
public virtual ActionResult Index() {
return View(MVC.MyArea.My.Views.MyView);
}
}
}
Note that in the second case, the token in 'My' instead of 'MyController', which is the way it always works.
Please try following those steps in a clean app to see if it works.
I believe my issue is being caused by T4MVC not rerunning. As a result (I speculate) the T4MVC does not update to reflect changes in your project. For example, changing the parameters of an Action in a Controller. Or (specific to this question) adding a new View.
The T4MVC documentation on getting itself to rerun is vague, but points you at a VS add in called Chirpy.
Chirpy installs, and then you have to go in an configure it. You do this by opening Visual Studio, then going to Tools >> Options and choosing the Chirpy option.
See the image:
I had to add the template name T4MVC.tt bit to get it to work. Not sure if this is necessary or why. But it all now works fine.
If there is a better way of doing this, please let me know.

ASP.NET MVC basic routing with no View name

This is a basic question about the routing machinery. From a new MVC project, I have this in the HomeController:
public ActionResult MyPage()
{
return View();
}
In the Views\Home folder, I have MyPage.aspx. The routing is still the default of {controller}/{action}/{id}. When I type in http://localhost:1790/Home/MyPage, it is correclty routes to MyPage.aspx. Since I haven't said anything about which view to render, how does ASP.NET MVC know to correctly route to MyPage.aspx? Looks as though the ActionResult name can also be used as the View/aspx page name...unless there is something I misunderstand in how the routing works. I can see how I end up in the Home folder, since the controller name corresponds to the View sub folder name. But does the Action name also correspond to the aspx name?
Would that work if the page was a PHP?
ASP.NET MVC subscribes to what is known as the Convention over Configuration paradigm whereas if you follow their conventions, basic things such as routing concerns will happen for you. But they also allow you to configure them if desired.
MVC implicitly assumes that if you return just View(), that you want View("MyPage") (i.e. the action name). No sense in repeating yourself unnecessarily.
It won't find a PHP file by default, but I'm sure you could override that behavior if you really wanted to. I can't imagine a sane scenario where you would be mixing PHP and ASP.NET MVC, but who knows :)
Action name is the same as the view / partial view name.
asp.net mvc doesn't work with php as far as I'm aware.
As has already been stated, ASP.NET MVC uses convention over configuration. Out of the box, your folder structure is something like this (only showing relevant portions and doing it from memory so...)
Site Root
+ Controllers
HomeController.cs
AccountController.cs
+ Views
+ Home
Index.aspx
+ Account
Index.aspx
+ Shared
The default routing handler is something similar to the following:
"{controller}/{action}/{id}"
There are default values for the route, but if you have a url that is a/b/c, it will look for action a on controller aController and pass it c as a parameter if said method on the controller accepts parameters.
A couple of things about that then need to be clarified. Again, convention over configuration:
1) All controller classes must end with Controller if you're using the default engine. That way, when a request comes in and the {controller} value is parsed, the engine adds Controller to it, looks in the Controller folder (and, thus, namespace), and locates the class.
2) By default -- this can be changed -- all views for a controller must reside in the Views/{controller} folder or in the Views/Shared folder.
3) Public methods on a controller are, by default, actions. You can hide this with an attribute to make themethod unavailable to the engine, but by default they are public.
So, when a request comes in the route is compared against all known routes (global.asax) and the first route that matches the request is accepted. The route is then parsed into the component parts to determine the controller, action, and parameters for the action.
Once the controller is identified, the engine instantiates an instance of that controller and executes the matching method (action) should it be found.
The action will return an ActionResult. View is an extensino method that actually returns ViewResult (if I remember that correctly). The default view for an action is a view of the same name as teh action residing in the Views/{ControllerName} folder.
Routing is a beast unto itself and I'd recommend a good bit of reading on it if you're going to play with it. Minutes to understand but a lifetime to master sorta thing.
To my knowledge, BTW, there is no engine that will use a php page as a view for a controller action.

Where to apply logic for a sidebar control in ASP.NET MVC

Take the example of wanting to have a "Latest news items" sidebar on every page of your ASP.NET MVC web site. I have a NewsItemController which is fine for pages dedicating their attention to NewsItems. What about having a news sidebar appear on the HomeController for the home page though? Or any other controller for that matter?
My first instinct is to put the logic for selecting top 5 NewsItems in a user control which is then called in the Master Page. That way every page gets a news sidebar without having to contaminate any of the other controllers with NewsItem logic. This then means putting logic in what I understood to be the presentation layer which would normally go in a Controller.
I can think of about half a dozen different ways to approach it but none of them seem 'right' in terms of separation of concerns and other related buzz-words.
I think you should consider putting it in your master page. Your controller can gather data (asynchronously, of course), store it in a nice ViewModel property for your view (or in TempData) and then you can call RenderPartial() in your master page to render the data.
The keeps everything "separate"
http://eduncan911.com/blog/html-renderaction-for-asp-net-mvc-1-0.aspx
This seems to address the question - even using the instance of a sidebar - but using a feature not included with MVC 1 by default.
http://blogs.intesoft.net/post/2009/02/renderaction-versus-renderpartial-aspnet-mvc.aspx
This also indicates the answer lies in RenderAction.
For anyone else interested, here's how I ended up doing it. Note you'll need to the MVC Futures assembly for RenderAction.
Basically you'd have something like this in your controller:
public class PostController
{
//...
public ActionResult SidebarBox()
{
// I use a repository pattern to get records
// Just replace it with whatever you use
return View(repoArticles.GetAllArticles().Take(5).ToList());
}
//...
}
Then create a partial view for SidebarBox with the content you want displayed, and in your Master Page (or wherever you want to display it) you'd use:
<% Html.RenderAction<PostController>(c => c.SidebarBox()); %>
Not so hard after all.
You can create a user control (.ascx) and then call RenderPartial().
Design a method in your controller with JsonResult as return type. Use it along with jQuery.
Use RenderAction() as suggested by elsewhere.
News section with ASP.NET MVC

Handling complex URLs in ASP MVC

I have - I think - a complex URL to deal with in ASP MVC 1.0:
All my actions in most of the controllers require two parameters all the time: Account and Project. This is on top of each Action's requirements. This means a typical URL is like this:
http://abcd.com/myaccount/projects/project_id/sites/edit/12
In this example:
myaccount is the account name. projects can be a controller, others options are like locations, employees. project_id is the id of a project within myaccount, sites could be a controller, other options are like staff or payments. edit is an action and 12 is the id of the site edited.
(hope this is clear enough)
Now one option is to create a route and pass project_id and account into all actions of controllers by adding two extra parameters to all actions. This is not really desired and also I'm not sure the two controllers (projects and sites) are going to work here.
My ideal situation is to use some kind of context that travels with the call to the controller action and store project_id and myaccount in there. The rest of the parameters then can be dealt with in a normal way like:
// sitescontroller
public ActionResult Edit(string id)
{
string account = somecontext["account"];
string project_id = somecontext["project"];
// do stuff
}
Any ideas as to how/where this can happen? Also how is this going to work with ActionLink (i.e. generating correct links based on this context)?
Thanks!
You first need to add the tokens to your routes like {company}/projects/{project}{controller}/{action}/{id}. Then if you wrote your own IControllerFactory then it would be very easy to push the values from the RouteData into the controller via the constructor or however you wanted to do it. Probably the easiest way to get started would be to subclass DefaultControllerFactory and override the CreateController method.
This doesn't quite make sense to me. Why would you have a route that is akin to the following:
{controller}/{id}/{controller}/{id}
?

Resources