I'm developing in asp.net mvc2. I am beginning to create a lot of views and partial views, for most of which I've had to create a view model. This is looking like soon it's going to become unmanageable to remember what view goes with what model. I've tried to use inheritance in my view models as much as possible.
I'm wondering how others manage this in their projects?
I place my PV's inside a PV folder within the Views folder.
so Views/Home/PartialViews;
I then register that path in my global.asax file;
public static void RegisterViewEngine()
{
ViewEngines.Engines.Clear();
WebFormViewEngine viewEngine = new WebFormViewEngine();
viewEngine.PartialViewLocationFormats = (new[] {
"~/Views/Shared/PartialViews/{0}.ascx",
"~/Views/{1}/PartialViews/{0}.ascx"
}).Concat(viewEngine.PartialViewLocationFormats).ToArray();
ViewEngines.Engines.Add(viewEngine);
}
I'm also these days leaning towards putting the FormViewModels within the views folder.
/Views/Home/IndexFormViewModel.cs
The above is recent as before this I put them in a Models project but found that sometimes I could end up with a cyclic reference situation with my Model and DataRepository.
Related
I have an MVC project that requires there to be 2 different View folders. One is at ~/Views/ and one at ~/Framework/Views/. This is done by creating a custom view engine based on the razor view engine like this:
public class MyViewEngine : RazorViewEngine
{
private static string[] AdditionalViewLocations = new[]{
"~/Framework/Views/{1}/{0}.cshtml",
"~/Framework/Views/{1}/{0}.vbhtml",
"~/Framework/Views/Shared/{0}.cshtml",
"~/Framework/Views/Shared/{0}.vbhtml"
};
public MyViewEngine()
{
base.PartialViewLocationFormats = base.PartialViewLocationFormats.Union(AdditionalViewLocations).ToArray();
base.ViewLocationFormats = base.ViewLocationFormats.Union(AdditionalViewLocations).ToArray();
base.MasterLocationFormats = base.MasterLocationFormats.Union(AdditionalViewLocations).ToArray();
}
}
The problem is that I want to use a different _ViewStart.cshtml file in each of the 2 Views folder (i.e. ~/Views/_ViewStart.cshtml for views found in the ~/Views/ folder and ~/Framework/Views/_ViewStart.cshtml for views found in the ~/Framework/Views/ Folder), however the View Engine just uses the first one it finds which is the original one in ~/Views/.
Is this possible to do?
Thank you
This is definitely possible, I think you just missed something.
I have tested this myself using the view engine you supplied (copied and pasted verbatim). I am not seeing the same behavior as you. I have two _ViewStart.cshtml files, one at ~/Framework/Views/_ViewStart.cshtml, and one at ~/Views/_ViewStart.cshtml.
When I run a view within ~/Framework/Views/, it uses the _ViewStart.cshtml in the Framework folder. When I run a view within ~/Views/, it uses the _ViewStart.cshtml in the Views folder.
Double checking the code in RazorViewEngine using DotPeek also confirms that this is exactly how it should behave. The view engine starts checking in for a file named _ViewStart.cshtml within the same folder as the view being rendered, and then walks up the directory tree until it gets to the root of the application.
The selection of _ViewStart is hierarchical, but you've added ~/Framework/Views parallel to ~/Views. I don't think Razor is set up to actually do what you want (i.e. two completely parallel view locations). If you were to put Framework into the main Views folder, your _ViewStarts would load properly, though.
My Views folder has gotten crazy big! I would like to reorganize it so that the Views folder contains a list of Modules, and then each Module folder contains its share of the View (Controller) folders that currently appear under Views folder.
But of course this means going into each of my controllers and editing every view-returning method the explicit location of its view.
So instead of Controller Orders.Index method just having this:
return View();
I have to edit it to return this:
return View("~/Views/Orders/Index.cshtml");
You can imagine the suck level that this exercise attains over 50 or so controllers.
Is there some way that I can setup a routing or something per controller that will tell that controller's methods to go find their views in a defined subfolder of the Views folder?
It can be done with the help of CustomViewEngine
Follow this post and i hope you can provide your own locations to locate the view template.
MVC provide way where we can easily provide list of path to be searched
Locate view
Once you add CustomViewEngine, register it in Application_Start() event and then you are done :)
Happy coding
You could fix that by implementing a custom RazorViewEngine, where you can specify the search path for the views per request, per controller and so on.
I want to create module based structure, Like in zend framework. Suppose I have 2 controllers like student and teacher and I want to put all these in one folder say shchool. Same way I want the view files for each controller in school folder for e.g
For Controller:
D:\aspprojects\Project1\Project1\Controllers\School\TeacherController.cs
D:\aspprojects\Project1\Project1\Controllers\School\StudentController.cs
and for view files:
D:\aspprojects\Project1\Project1\Views\School\Teacher\all CRUD files(*.cshtml)
D:\aspprojects\Project1\Project1\Views\School\Student\all CRUD files(*.cshtml)
Current structure is like as below,
for Controllers:
D:\aspprojects\Project1\Project1\Controllers\TeacherController.cs
D:\aspprojects\Project1\Project1\Controllers\StudentController.cs
For view files
D:\aspprojects\Project1\Project1\Views\Teacher\all CRUD files(*.cshtml)
D:\aspprojects\Project1\Project1\Views\Student\all CRUD files(*.cshtml)
What changes do I need to do?
The problem you are facing is that MVC doesn't care what folder the controller is in. In fact, it doesn't even have to be in a folder called Controllers. MVC only looks for classnames with Controller in the name. Once compiled, the folder structure is lost, and as such, no way for the framework to know to look in a subfolder for a view, because that information is no longer present in the compiled code.
You can still do it, however.. but you can no longer rely on MVC to find your view files automatically, you will have to pass each view name directly.
This means you will have to do this:
return View("~/Views/School/Teacher/Index.cshtml");
Rather than this.
return View();
Another option is to use areas, which allows you to create a School area, and then you can create a teacher and student controllers within the school area.
I've been working with ASP.Net MVC (3) for some time now and i like it a lot. But one thing i find a bit annoying is having to browse between the controllers / views / model / script directory all the time. So i'm wondering if there's a way to tell MVC to look for the files in a different location?
Maybe someone can tell me how to simply group the files together by controller like:
Directory: /Membership
MembershipController
LogOnView
LogOnModel
RegisterView
RegisterModel
Kind regards
Olav
I know exactly what you're talking about. Here are the conditions where I find the default MVC folder structure to be onerous:
I'm using a model-per-view approach
My controller basically only works with that one particular view
I have some javascript that only pertains to that view
Why do I want to put each of these pieces in a different folder?
I create a folder for the view in the Views folder, so you have a folder ~/Views/MyEntityList (just like the traditional MVC approach), but I put everything that pertains to that component there:
~/Views/MyEntityList/
MyEntityListController.cs
MyEntityListModel.cs
MyEntityList.js
MyEntityList.aspx
I find this structure leads all the developers to keep views decoupled from one another. No special MVC configuration is required, except for allowing browsers to access the .js resources directly.
There are some architectural patterns where this might not be a good way to go. For a model-per-view approach (see Los Techies for more description) I really like this structure.
I think you need to get the Solution Navigator extensions via Power Tools update for VS 2010.
That way, you can display in the Solution Navigator, as opposed to the solution explorer, only the open files, for example. Makes it easier.
By the way, delete all the model folders and create a separate model project, eg:
MyApp.Domain
Any solution that is beyond basic will benefit from this.
As stated in the comments to your question, Areas will also reduce your navigation requirements.
The only "looking of files" going on is with views, everything else is just a convention, so if you want you could have:
Directory: /Membership
MembershipController
LogOnView
LogOnModel
RegisterView
RegisterModel
... but the views must be in ~/Views/Membership
It looks like you have to override some behavior in the view engine. You can See this question to get a better idea.
One way I can think of to achieve this is by writing your custom view engine. You can place all these below files in Controllers/Membership
MembershipController
LogOnView
LogOnModel
RegisterView
RegisterModel
Models will not be a problem you can simply change the namespace for the models, the only problem is with the views. For this write your custom view engine so that your mvc application knows the physical location of the view files as follows.
public class CustomViewEngine : RazorViewEngine
{
public CustomViewEngine()
{
ViewLocationFormats = new[]
{
"~/Controllers/{1}/{0}.cshtml",
};
}
}
In global.asax.cs add the ViewEngine in Application_Start() by including the following code
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new CustomViewEngine());
You may also have to take care of various other factors like updating the Layout attribute depending on where you place the _Layout.cshtml.
In case you are using areas, add the AreaViewLocationFormats string array as well.
You can do further customization by overriding some of the methods like FileExists, CreateView, CreatePartialView.
Note: Do not forget to copy web.config in the views folder to the Membership controller. Otherwise application does not find the required mvc namespaces and it does not find the symbols like viewbag, model etc.
My question may be obvious but I'd like to build a well-designed web application.
As for any administration area, the admin should be able to list/create/delete/modify users, articles, posts, etc...
I'd like to know what is the best way to design the application.
Should I create a controller for each one of those items (/Users/Create/id or /Posts/Delete/id), or create all of the action in my Administration Controller (/Administration/CreateUser/id or /Administration/DeletePost/id) ?
You should write a separate controller for each entity to keep a clean separation of concerns for your controller classes. If you only have one controller, then you will only have one Views directory with dozens of views, and your controller will contain dozens of methods, and that will soon become unmanageable.
The answer depends on how much functionality will be in the controllers. Just start of with one controller and if it gets too much split it into a few.
The great thing about MVC is where you put things in your controllers doesn't have to have any effect on the URLs. you can very easily map /Users/Create to e.g. UserAdminController class.
I would just build a new MVC website that handles the administration.
You have a more flexible solution as long as you've separated the data and the business logic in different assembly's. You could then publish your website to a subdomain, admin.yoursite.com for example. That way you don't have to mess with your routes and you can keep them in separate views which imho is the most elegant solution.
Pro's and Con's would be nice to hear.
I'm working on a project which will need the same administration site but haven't got that far yet so this question interests me a lot.
I'm currently using ASP.NET for a large client.
The approach I've adopted is to put the functionality of the action into another class.
Example
I am writing an administration section also. There will be one Administration controller (our admin section is small, if it was larger I would change the routing to allow more controllers, for now we are using the out of the box configuration). If I create an "EditUser" view. I will also create an "EditUserAction" class. All EditUser code will go into the class. I construct the EditUserAction class in the Administration controller class in the Edit User method. This removes all the action specific code out of the Controller class. This way all the action specific code is either in the action method or in the action class. Otherwise, the controller would quickly become overrun with code from various actions. The controller class would balloon to an unmanageable mess in short order.
Class examples
public class Administration: Controller
{
public ActionResult EditUser(string userId)
{
EditUserAction action = new EditUserAction();
}
}
public class EditUserAction
{
public void Save(User user)
{
//save code here
}
}
I hope this explanation is clear. If it's not let me know and I'll clarify.
To answer your question I am doing it the latter(/Administration/CreateUser/id or /Administration/DeletePost/id).
You could use DynamicData for this. It isn't MVC but it can be used together with it and it is really easy to setup and use.
Here is another way of asking my question.
A part of my Master Page:
<% if (!String.Equals(ViewContext.RequestContext.RouteData.GetRequiredString("controller"), "Administration")) { %>
<div>
<!-- Some Code -->
</div> <% } %>
As you can see, in my master page, I'd like to display some part of the page, depending on the user working on the administration area or not.
It works pretty well with only the Administration Controller (/Administration/CreateUser/id)... but it becomes a big mess when I use different controller as User or Article (/User/DeleteUser/id or /Article/Details/id).
I'd prefer to use one controller per entity, but I can't find a way to bond this approach with multiple controllers.
I suggest to use this solution.
But I changed definition to this:
public ThemedViewEngine()
{
base.MasterLocationFormats = new string[] {
"~/Views/{1}/{0}.master",
"~/Views/Shared/{0}.master",
"~/Themes/{2}/Views/{1}/{0}.master",
"~/Themes/{2}/Views/Shared/{0}.master",
"~/Themes/Default/Views/{1}/{0}.master",
"~/Themes/Default/Views/Shared/{0}.master"
};
base.ViewLocationFormats = new string[] {
"~/Views/{1}/{0}.aspx",
"~/Views/{1}/{0}.ascx",
"~/Views/Shared/{0}.aspx",
"~/Views/Shared/{0}.ascx",
"~/Themes/{2}/Views/{1}/{0}.aspx",
"~/Themes/{2}/Views/{1}/{0}.ascx",
"~/Themes/{2}/Views/Shared/{0}.aspx",
"~/Themes/{2}/Views/Shared/{0}.ascx",
"~/Themes/Default/Views/{1}/{0}.aspx",
"~/Themes/Default/Views/{1}/{0}.ascx",
"~/Themes/Default/Views/Shared/{0}.aspx",
"~/Themes/Default/Views/Shared/{0}.ascx"
};
base.PartialViewLocationFormats = new string[] {
"~/Views/{1}/{0}.aspx",
"~/Views/{1}/{0}.ascx",
"~/Views/Shared/{0}.aspx",
"~/Views/Shared/{0}.ascx",
"~/Themes/{2}/Views/{1}/{0}.aspx",
"~/Themes/{2}/Views/{1}/{0}.ascx",
"~/Themes/{2}/Views/Shared/{0}.aspx",
"~/Themes/{2}/Views/Shared/{0}.ascx",
"~/Themes/Default/Views/{1}/{0}.aspx",
"~/Themes/Default/Views/{1}/{0}.ascx",
"~/Themes/Default/Views/Shared/{0}.aspx",
"~/Themes/Default/Views/Shared/{0}.ascx"
};
}
Default theme is default so it is required to exists.
Structure of directory will be:
Content
Themes
Default
Content
Views
Home
Blog
Whatever should be skinned
OtherTheme
Content
Views
Home
Blog
Whatever should be skinned
Views
Articles
Posts
Users
Settings
Other Administration stuff
It depends on the scale size of your admin area,
I suggest you consider to do the following (or maybe document it a bit)
Consider how many entities you want to manage independently,
Consider how many actions each one will have,
Check that is there any dependency between your application and admin area (user access, for user friendly URLs )
then you can specify which approach can help you, Having one admin controller, admin actions in entity controllers, or defining a new Admin project in case of large functional applications.
*If the project scale is growing fast and soon it needs large scale, I would choose third one - having a new admin mvc project.
I hope it help you decide.