Coming from a long period of WebForms development, I recently began working with MVC. One thing that struck me, is that since controller methods are not called directly from code, Visual Studio won't pick up any references to them.
I understand the reason why there is no reference count, but it can be problematic sometimes. Let's say I rewrite a controller method to accomodate some new requirement, perhaps a certain call to the method needs additional data or a modified result. But perhaps that method, or endpoint if you will, is being called from several places, and the change I made breaks the result handling for those other calls. I would like to be able to easily find all the code in my solution, which is going to invoke the endpoint.
Currently I tend to copy the method name and perform a regular text search in the entire solution, which works rather well as long as the method name isn't too generic. If the method name is "Index", it can be a long day.
Are there any recommendations on how to simplify this, such as coding conventions, plugins or otherwise? I.E. how can I locate from where the endpoint will be invoked?
I currently use Visual Studio 2017 Enterprise, but solutions working in other versions as well are preferred - to make the answer useful for as many as possible.
Thanks in advance / Culme
UPDATE: I learned a lot based on the comments and replies I got. Apart from what is mentioned below, I also decided to try and keep my controller method names unique and identifiable, to simplify finding where they are being used. As an example, instead of just "Index", I'll try to use "Fidgets_IndexController" or something similar. That way, a simple text search will go a long way for locating calling code.
Controller methods are not directly referenced from any part of the code (therefore 0 references), but they are dynamically invoked based on RouteTable which maps controller methods on startup RouteConfig.RegisterRoutes(RouteTable.Routes); in global.asax "implicitly" which would map them as /controller_name/action_name or these can be changed by either editing
RouteConfig.RegisterRoutes(RouteCollection routes) or using attributes:
[Route("myAction")]
public ActionResult MyAction() {
...
}
which would bind it to /myAction (without controller name)
further reading:
MSDN - Understanding MVC Application Execution Process
Lifecycle of an ASP.NET MVC 5 Application
ASP.NET MVC Routing Overview
One of the options is Resharper - it more or less can determine where you use Html.ActionLink() or Html.BeginForm() that points to a controller action. But it won't detect any posts/redirects done from JS.
Also another thing you can do is use T4MVC to make the links from views statically typed, so you can search on references.
Text search through the solution can help, but not always good as you already discovered - how many references to string Index do you have in an average MVC project? So this will help with distinctive controller/action names, but not with common names.
Apart from that you are on your own. If you try doing something clever, like in JS concatenate strings to give you the right endpoint - you are in trouble.
Another option if you know the application fairly well. Put a breakpoint on the controller action and then run some tests of the application.
Related
I've started a new MVC project, and this is my first where its pure MVC without CMS integrations or otherwise. I'm looking for advice on naming conventions, as I am aware there are going to be a lot of controls and forms that are re-used.
Is it sensible to create say a Controls Controller for these re-usable items and call them from my views as appropriate? Also, would there then be a way to stop someone navigating to that Controls/MyControl URL directly?
Thanks,
Mike.
To stop one from navigating to Controls/MyControl you just need to add ChildActionOnly attribute:
[ChildActionOnly]
ActionResult MyControl(...){...}
Naming/grouping is up to you - you can start with one controller and see if controls need to be split into groups later.
Note that you may not even need controller for most of the controls as long as they just show data - #Html.RenderPartial("MyControl", dataForControl) may be enough (assuming Razor and existence of "MyControl.cshtml")
I am transitioning to using Areas in a project, and each time I make a new one I have to change a few things to make it fit in with how I'd like things to work. The changes involve:
Removing one of the folders that gets created
Modifying the web.config file slightly
Adding a new file or two with some defaults
Modifying the AreaRegistration file slightly
I would rather not have to do these same things every time I create a new area because I'm afraid that the convention will be forgotten and something will get messed up. Is there a way to modify the defaults for New > Area? Or perhaps would it be possible to make a new file template that does what I'd like?
I am transitioning to using Areas in a project
I have been doing something similar for a few months now, but starting with an old WebForms project, adding MVC components via VS 2013's Add -> New Scaffolded Item... feature, in order to leverage MVC's structure and routing, on the server side. I have also been incorporating WebAPI and SignalR on the server side. I am definitely still a beginner, but being maybe a little further along in a similar process, here some of my thoughts and questions about what you want to achieve:
Removing one of the folders that gets created
Modifying the web.config file slightly
Adding a new file or two with some defaults
What environment are you developing in? For instance, if you're using VS 2013's scaffolding you can most likely modify the T4 templates to prevent or redirect the creation of folders, classes, and such. Here is a post addressing where you might find those templates, and here is just one of many intros out there for T4. The path to the templates on your machine will vary based on the VS version. By modifying or creating new templates you should be able to accomplish all 3 points above relatively easy. If you are working in such an environment I will try to expound on exactly how you might go about modifying the out of the box templates (there are probably already plenty of posts that you could refer to which would do a better job though).
Modifying the AreaRegistration file slightly
If you are working with MVC 5, or later, I would recommend using attribute routing. This seems more standard when working within the API paradigm (inheritting from ApiController instead of Controller), but I have found extending this to traditional MVC controllers to be very useful.
Assuming MVC 5, I would recommend modifying AppStart\RouteConfig.cs's RegisterRoutes() method, to enable attribute routing and then add a catch-all route such as:
routes.MapMvcAttributeRoutes();
routes.MapRoute(
"NotFound",
"{*url}",
new {
controller = "Error",
action = "Index"
});
At that point I would delete the xAreaRegistration.cs files, and remove the call to RegisterAllAreas() from Global.asax, but you don't have to go that far. Then I decorate my controllers' classes (wherever they live) and their methods, with attributes as needed. Using attribute routing I've been able to free myself from the traditional MVC conventions. I can still fall back to them, but I can then place my controllers anywhere in my project, and easily define their actions' routes.
That being said, you should probably follow some organizational standards for groups of similar routes, so that as the project grows you don't find yourself wasting a lot of time searching for all controllers/actions matching some set of routes.
Here's a simple example of how this actually translates from URI into method call:
If I had Controller ctl with action act, logically in Area ar, the URI being http://localhost/ar/ctl/a/1, would be routed to MyControllerNameDoesNotMatter.Index(1), below, by using attribute decoration such as:
[RouteArea("ar")]
[RoutePrefix("ctl")]
public class MyControllerNameDoesNotMatter : Controller {
...
[Route("a/{optionalParamDefaultsToNegativeOne=-1}")]
public ActionResult Index(int optionalParamDefaultsToNegativeOne) {
...
}
}
This way of looking at Area is more logical than physical, in that the MyControllerNameDoesNotMatter.cs file does not have to be in the Area\ar\Controllers folder. Since adopting this I have actually drifted away from using the Area scaffolding, other than to lay out application modules at a very high level - think sub-application that could be a stand alone SPA.
I have tried to make the "catch-all" route attribute based, and remove it from RouteConfig.cs, but I have not been able to do this successfully. The issue is route precedence. It works fine as a catch-all for invalid routes. But for a valid route, multiple (2) routes end up being matched because the two attribute routes have the same order of precedence. Whereas, with the catch-all defined in RouteConfig.cs, after registering all attribute routes, the attribute route takes precedence. I have found mixed answers that attempt to address this issue. There seems to have been a Number or Order parameter for the Route attribute at some point, but I have had no luck with that. This seems to be an unresolved issue. Just one of many SO questions, which have gone unanswered.
Edited to account for mvc4 tag
I had not seen the mvc4 tag prior to posting. There are nuget packages available that will achieve much of the same functionality, such as scaffolding and attribute routing for earlier versions of MVC.
So I am reading about building MVC3 projects and there is one thing that seriously bugs me. The folder structure of the project in no way corresponds to the path of the HTTP request. There is a bunch of things I like and would want to use, but having a flat folder structure is not one of them.
Why is this a problem? Well, I see it becoming a problem when building a site with a heavy mix of content pages and various forms/dynamic pages (most of our sites are like that), which would typically be done by different people. It seem it would be too complicated for client-side developers to follow routing rules of dynamic pages and/or creating new ones.
What I would like to understand is if there is way to configure MVC3 application in such a way that:
it follows directory structure for finding controllers without explicit route map for each one
views live in the same folder as corresponding controller
routing magic still works for actions and parameters
For instance I'd like to have a request /fus/ro/dah/ to try to find DahController in the \webroot\fus\ro\dah\ folder and execute its Index action. If not found it would look for RoController with Dah action in the \webroot\fus\ro\ folder, etc.
It is entirely possible that MVC was not meant to be working this way at all and I am just trying to force a square peg into a round hole.
UPDATE:
Looks like I can drop a view file into the desired folder structure, and it will be executed. However layout would not work apparently because it is expecting a controller. Does this mean I have to create a controller for pure content pages? That is a pretty crappy design...
UPDATE 2:
Main issue right now is that creating "fus" folder means that MVC will not even attempt to look for FusController... not under "fus" folder, nor anywhere else. Is it possible to get around that?
For instance I'd like to have a request /fus/ro/dah/ to try to find
DahController in the \webroot\fus\ro\dah\ folder and execute its Index
action. If not found it would look for RoController with Dah action in
the \webroot\fus\ro\ folder, etc.
MVC is not designed for a particular need like this, it is a general framework for building applications using model-view-controller pattern.
If you can't bend the application for the framework you can bend the framework for the application and honestly MVC is very customizable. [As a proof, in the current project (migration from ASP to MVC) that I'm working we have models as xml and no classes also we are using XSLTs for rendering. With a little work we have created custom components like custom view engine, custom validation provider, custom model binder... to make the framework best fit for the application and it does]
MVC is not designed and not forces to use it as it is and you can customize/extend as much you want. In your case you may have to create a
custom controller factory (because you want to customize the way in which the controller is seleced),
custom view engine (because you want to customize where the view is placed)
and may be others.
For custom controller factory you have to extend the DefaultControllerFactory class. There are lot of articles you can find through Google that explains about how to create custom controller factories.
Depending upon the view engine you are using you have to extend the respective one. For ex. if you are using web forms then you have to extend the WebFormsViewEngine and it razor then RazorViewEngine.
For more info. check this link
http://codeclimber.net.nz/archive/2009/04/08/13-asp.net-mvc-extensibility-points-you-have-to-know.aspx
you can mixup Asp.net and Asp.net MVC. as LukLed said, MVC is convention over configuration pattern. if you follow the convention. you dont need to configure. you can check this link for mixing up the asp.net content with MVC3
Mixing Asp.net and Razor
I believe ASP.NET MVC is not meant to be used that way. While you can configure MVC to do it, it is better to keep standard /controller/action/parameters URL format. If you have complex website with many different functionalities, areas may be helpful http://msdn.microsoft.com/en-us/library/ee671793.aspx. Every area get its own set of controllers, models and views, so teams working on different parts of website won't disturb each other.
While it may sound convenient, that framework first searches for DahController and executes Index action, then searches for another one, I find it bad idea. URLs should be clearly defined and Fus controller with Ro action shouldn't just stop working, because someone created RoController with Index action.
Look into using Areas as well. I think that helped me get over my folder structure issues with MVC honestly. So i could use the base folder as my Home details, then i could have a 'Admin' area which was a separate folder, things like that.
How "regular ASP.net" do you want it to be? If you want to do "legacy" ASP.Net Web Forms mixed in with MVC, you certainly can - re: mixing MVC with "file based aspx" - aka "hybrid". At the end of the day, it's all ASP.Net.
This is a standard MVC Application generated by Visual Studio. I've added a folder somedirectory where I want to use the legacy folder/file.ext paradigm and have a default.aspx Web Forms file:
Can I navigate to it via http://foo.com/somedirectory? Yes.
Can I use "pretty urls" too? Yes
Vanilla Global.asax generated by VS, just added MapPageRoute:
....
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//using "pretty urls" - ordering your routes matter.
routes.MapPageRoute("theWebForm", "legacy", "~/somedirectory/default.aspx");
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
so now I can navigate to it via http://foo.com/legacy
Just check the order of your routes, and perhaps plan on your naming conventions so you don't have "collisions"...
Hth....
My question is similar to "ASP.NET 2 projects to share same files", but with an ASP.NET MVC slant.
Basically, we have two sites, one being based mostly on the other (roughly 90% views, controllers, images, in the second are identical to the first). However, in some cases, the views may be different, or a controller in the second site may be different to the first.
Are there any simple ways of achieving this in ASP.NET MVC?
So far, we've looked at using linked files to have two totally seperate projects where the second project shares the files it needs from the first.
One problem with this approach is that most pages in the second project don't literally exist in the virtual directory, it makes debugging a pain - you have to publish in order to generate the files so you can debug.
Does anyone have any better approaches, or ways this approach can be simplified?
This article might help: http://dotnetslackers.com/articles/aspnet/storing-asp-net-mvc-controllers-views-in-separate-assemblies.aspx
Essentially, it involves creating your own WebFormViewEngine which tells MVC where to look for the Views.
If they are 90% the same and you want to keep them in sync as functionality evolves I would consider making them into a single web application and using the host header to differentiate and to change the images/links/text/functionality/etc. between the two sites.
You can do this in your BaseController: look at the host header and create a Brand object that every page and view has access to, just like it might have aUser object. That Brand object can include the name of the site, the canonical Url for the site, the location of the image directory for that brand, boolean values to turn certain features on or off, ...
Now in a Controller you can just ask if (Brand.SupportsPageX) {...} to decide what to show next. And if you make the brand object part of your base view model or put it in the view collection you can have views that use the brand's name, image directory, ... to customize how they look.
Put as much as possible into a shared non-UI library project. I do that on every project that I work on this, increased testability, shared code with Windows Services, and a host of other reasons.
I've found that if you're aggressive with this then quite often over two-thirds of the project would be in the non-UI shared library.
After doing this, you can take things a step further and create a second library with some shared MVC/UI elements.
Depending on your opinion of it, RenderAction may help a bit here. As you know, RenderAction will allow you to group up those controller/view components and pass in variable arguments at runtime.
A project I'm working on currently has a similar requirement and we have started looking at using portable areas. I personally haven't delved very deeply into them at the moment, but it may be worth you taking a look.
I've been working with areas to create multiple websites that I can host with one hosting account. I would think you could use a similar approach. By implementing the common functionality in the base project, it will be available to each area. You can also override any of the base views or controllers by implementing them in the area. It may require some tweaking of the ViewEngine and Routing but I think it could be done.
In my web application I have a treeview with documents. The NavigateUrl of a treenode looks like this:
viewDocument.aspx?id=1&doctype=type
In the load event of ViewDocument.asp.cs I check whether id and doctype are set. If so, check doctype has a valid value and check the id is a number. After that, i create a document object. Then I call it's Load(int id) function which will load all the data in the object. If the id doesn't exists in the database the return value is false, otherwhise true.
Is it possible to use MVC pattern? And if so, how do I start?
There is an official ASP.NET MVC framework. Check it out here. You can read up the tutorials there to get you started.
It is, but MVC is the second issue you have to address.
It would make sense if you used URL rewriting here to rewrite your urls into this template:
documents/type/id
Here is a good blog post on how to enable URL rewriting for your site hosted in IIS7:
http://blogs.iis.net/bills/archive/2008/05/31/urlrewrite-module-for-iis7.aspx
Once you have that in place, MVC is an EXCELLENT candidate for handing this scenario. You would simply declare a route with this pattern:
{controller}/{action}/{id}
The controller would be a class (most likely, DocumentsController), the action would be the type in this case. You don't have to use that, you could use a type, but then you would have to set a default action when setting up the route. Finally, the id would be a parameter into the method that is specified by action.
Start by looking into what the MVC pattern is and what it buys you. To really use the MVC pattern, and not just superficially implement some framework, you will need to get your hands around the concept rather than just reading through the framework's API.
Some of the things MVC can buy you (though not exhaustive):
- decoupling actual display from flow control and business logic
- all the -ity's:
modularity
readability
maintainability
testability
Though to get all these advantages, you have to understand the MVC concept and apply it correctly, not just arbitrarily include a framework in your project and then expect everything to go right.
Here's a good starting point for MVC:
http://en.wikipedia.org/wiki/Model-view-controller