Overview
Large Web applications require better organization than small Web applications. In large applications, the default organizational structure used by ASP (and Core MVC) begins to be a burden to you. You can use two simple techniques to update your organization's methods and follow up on growing applications in a timely manner.
The Model-view-controller (MVC) pattern is quite mature, even in Microsoft ASP. The first version of ASP. NET MVC was launched in 2009 and launched the ASP. NET Core MVC platform in the early summer. So far, with the improvement of ASP. NET MVC, the default project structure has remained the same: the Controllers and Views folders, and usually the "model" (or possibly the ViewModels) folder. In fact, if you create a new ASP. NET Core app now, you'll see that these folders are created by default templates, as shown in Figure 1 .
Figure 1 Default ASP. NET Core Web app template structure
The organizational structure has many advantages. It's familiar, and if you've been using an ASP. NET MVC project for the past few years, you'll soon recognize it. It's organized, and if you're looking for a controller or view, you'll know exactly where to start. When you start a new project, the organizational structure works well because there are not many files. However, as the project grows, the friction of viewing the files in the number of files and folders that are required to locate the desired controller or in these layers increases.
To understand what I mean, imagine that you organize your computer files in this same structure. You don't have separate folders for different project or work types, but only directories that are entirely organized by file types. There may be folders for text documents, PDFs, images, and spreadsheets. When you perform special tasks that contain multiple document types, you will need to bounce back and forth between different folders and scroll and search through many files in each folder that are not relevant to the current task. This is how features in an MVC app are used in the default way.
The problem with this approach is that filegroups that are organized by type (rather than by purpose) are often missing aggregations. Aggregation refers to the degree to which elements of a module belong together. In a typical ASP. NET MVC project, the given controller will refer to one or more related views (in the folder corresponding to the controller's name). Both the controller and the view will refer to one or more ViewModel related to the responsibility of the Controller. However, typically, ViewModel types or views are rarely used by multiple controller types (and typically, the domain model or persistence model is moved to its own individual project).
Sample Project
Consider a simple project (a task that manages 4 loosely related application concepts): Ninjas, Plants, Pirates, and Zombies. The actual example only allows you to list, view, and add these concepts. However, imagine that there are other complexities involved in more views here. The default organizational structure for the project should look similar to Figure 2.
Figure 2 Example project using the default organization
To use some of the new features that contain Pirates, you need to navigate to controller and find Piratescontroller, then navigate from view to Pirates and the corresponding view file. Although there are only 5 controllers, you can see that there are many folders that move up and down the navigation. This problem is more serious when the root of the project contains more folders, because the controller and view are not arranged alphabetically near each other (so other folders tend to be placed between the two folders in the Folder List).
An alternative way to organize files by file type is to organize them by application execution. Your project organizes folders around features or areas of your organization to replace folders organized by controllers, models, and views. When you work with bugs or features related to a particular feature of your app, you need to keep very few folders open because the related files may be stored together. There are several ways to do this, including using the built-in areas feature for feature folders and using your own conventions.
How ASP. NET Core MVC views files
We need to take some time to talk about how ASP. NET Core MVC works with the standard type of files built into the application it uses. Most of the files involved in the server side of the application will be categorized in some. NET languages. As long as they can be compiled and referenced by the application, these code files can exist anywhere on the disk. Specifically, the Controller class files do not need to be stored in any particular folder. The various model classes (domain models, view models, persistence models, and so on) are all the same, and they can all easily exist in separate projects in the ASP. NET MVC Core project. You can arrange and rearrange most of the code files in your application in any way you want.
However, the view file is different. A "View" file is a content file. They are not related to where the application's controller classes are stored, but MVC needs to know where to find them, which is important. Areas provides built-in support for locating views in different regions compared to the default views folder. You can also customize the way MVC determines the view location.
Organizing MVC projects with areas
Areas provides a way to organize independent modules within an ASP. NET MVC application. Each area has a folder structure that simulates the project's root contract. Therefore, your MVC application should have the same root folder contract and an extra folder called areas, which contains a folder for each part of the app, which includes the "controller" and "View" folders (which may also include the "model" or "ViewModels" folder, as appropriate).
Areas has powerful features: it allows you to subdivide a large application into separate and logically different sub-applications. For example, a controller can have the same name across regions, and in fact, it is common to have homecontroller classes in each region within an application.
To add support for areas for an ASP. NET MVC Core project, simply create a new root-level folder named "Areas". In this folder, create a new folder for each part of the application that you want to organize in area. Then, within the folder, add a new folder for controller and view.
Therefore, your controller file should be located at:
/areas/[area Name]/controllers/[controller Name].cs
Your controller needs to have the area properties applied to it so that the framework knows that they belong to a particular region:
namespace withareas.areas.ninjas.controllers{ [Area ("Ninjas")] publicclass Homecontroller:controller
Your view should then be located at:
/areas/[area Name]/views/[controller name]/[action name].cshtml
Any links that have been moved to a view in the zone should be updated. If you are using the tag helper, you can specify the zone name as part of the markup helper. For example
<asp-area= "Ninjas" asp-controller= "Home" asp-action = "Index">Ninjas</a>
Links between views in the same area can omit the Asp-area property.
The last thing you need to do for a zone that supports your app is to update the default routing rules for the applications in Startup.cs in the configuration method:
app. USEMVC (Routes =>{ // areas support routes. MapRoute (name: " arearoute " , Template: " {area:exists}/{controller=home}/{action=index}/{id?} " " default " , Template: " {controller=home}/{action=index}/{id?} "
For example, a sample application that manages various Ninjas, Pirates, and so on can take advantage of areas to implement the project's organizational structure, as shown in Figure 3 .
Figure 3 Using the areas organization ASP. NET Core Project
In contrast to the default conventions, the areas feature provides improvements by providing separate folders for each logical part of the application. Areas is a built-in feature in ASP. NET Core MVC, which requires minimal setup. If you haven't used them yet, remember that they are a simple way to group the relevant parts of your app together and detach from the rest of your app.
However, the areas organization's folders are still heavily loaded. You can see this in the desired vertical space, which shows the relatively small number of files in the areas folder. If you don't have a lot of controllers in each region and you don't have a lot of views in each of your controllers, the above folder is almost the same as using the default conventions.
Fortunately, you can easily create your own conventions.
feature folders in ASP. NET Core MVC
In addition to the default folder conventions or the use of built-in areas features, the most popular way to organize MVC projects is to use files for each feature. This is especially true for teams that have adopted delivery capabilities in a vertical subdivision (see http://deviq.com/vertical-slices/), because most of the vertical subdivision of user interface concerns can exist in any of the feature folders.
When you organize your project through features (rather than through file types), you typically have a root folder (such as "features"), which has subfolders for each feature. This is very similar to the way the organization areas. However, within each feature folder, you will include all the required controllers, views, and ViewModel types. In most applications, this causes 5 to 15 items in a folder, all of which are closely related. The entire contents of a feature folder can be kept in Solution Explorer for viewing. You can see an example of the Organization for this sample project in Figure 4 .
Figure 4 Functional Folder organization
Note that even the root-level controllers and Views folders are eliminated. The home page of your app is now in its own feature folder named Home, and shared files, such as _layout.cshtml, are also located in the shared folder within the features folder. The project's organizational structure is well-developed, allowing developers to focus on fewer folders as they make a particular part of the application.
In this example, unlike using areas, other routes and properties of the controller are not required (but it is important to note that the controller name must be unique among the features in the implementation). To support this organization, you need to customize Iviewlocationexpander and Icontrollermodelconvention. Use both with some custom viewlocationformats to configure MVC in your startup class.
For a given controller, it is useful to know what function it is associated with. Areas does this by using properties, and the method uses conventions. The contract settings controller is located in a namespace named function, and the next item in the namespace layer after function is the feature name. The name is added to the property (available during the view location process), as shown in Figure 5 .
Public classfeatureconvention:icontrollermodelconvention{ Public voidApply (Controllermodel Controller) {controller. Properties.add ("feature", Getfeaturename (controller. Controllertype)); } Private stringgetfeaturename (TypeInfo controllertype) {string[] tokens = ControllerType.FullName.Split ('.'); if(!tokens. Any (t = = T = ="Features"))return ""; stringFeatureName =tokens. SkipWhile (t=!t.equals ("features", Stringcomparison.currentcultureignorecase)) . Skip (1) . Take (1) . FirstOrDefault (); returnFeatureName; }}
Figure 5 Featureconvention:icontrollermodelconvention
When you add MVC to your startup, add the Convention as part of Mvcoptions:
Services. ADDMVC (o = O.conventions.add (new featureconvention ()));
To replace the normal view location logic used by MVC with a feature-based convention, you can clear the viewlocationformats used by MVC and replace it with your own list. The operation is performed as part of the ADDMVC call, as shown in Figure 6 .
Services. ADDMVC (o = O.conventions.add (Newfeatureconvention ())) . Addrazoroptions (Options= { //{0}-Action Name//{1}-Controller Name//{2}-area Name//{3}-Feature Name//Replace Normal View location entirelyoptions. Viewlocationformats.clear (); Options. Viewlocationformats.add ("/features/{3}/{1}/{0}.cshtml"); Options. Viewlocationformats.add ("/features/{3}/{0}.cshtml"); Options. Viewlocationformats.add ("/features/shared/{0}.cshtml"); Options. Viewlocationexpanders.add (NewFeatureviewlocationexpander ()); }
Figure 6 Replacing the normal view position logic used by MVC
By default, these format strings contain placeholders for operations ("{0}"), Controllers ("{1}"), and Zones ("{2}"). The method adds the 4th tag of the feature ("{3}").
The view location format used should support views that have the same name but are used by different controllers within the feature. For example, it is common to have multiple controllers in a feature, and multiple controllers have an index method. This feature is supported by a search view in a folder that matches the controller name. Therefore, Ninjascontroller.index and Swordscontroller.index should be in their respective/features/ninjas/ninjas/index.cshtml and/Features/Ninjas/ Locate the view in swords/index.cshtml (see Figure 7).
Figure 7 Multiple controllers per function
Note that this feature is optional-if your feature does not need to differentiate the view (for example, because the feature has only one controller), simply place the view directly into the features folder. Similarly, if you prefer to use a file prefix than a folder, you can easily adjust the format string from "{3}/{1}" to "{3}{1}" for use, resulting in view filenames such as ninjasindex.cshtml and swordsindex.cshtml.
Shared views are supported in both the root and shared subfolders of the feature folder.
The Iviewlocationexpander interface provides a Expandviewlocations method (the framework uses this method to identify the folder that contains the view). These folders are searched when the operation returns to the view. This method only requires Viewlocationexpander to replace the "{3}" token with the Controller's feature name (specified by the featureconvention mentioned earlier):
Publicienumerable<string>expandviewlocations (viewlocationexpandercontext context, IEnumerable<string>viewlocations) { //Error checking removed for brevity varControlleractiondescriptor =context. Actioncontext.actiondescriptor asControlleractiondescriptor; stringFeatureName = controlleractiondescriptor.properties["feature"] as string; foreach(varLocationinchviewlocations) { yield returnLocation. Replace ("{3}", FeatureName); }}
To properly support publishing, you also need to update Project.json's publishoptions to include the features folder:
"publishoptions": { "include": [ "wwwroot", " views", "areas/**/*.cshtml", "features/**/*.cshtml", "Appsettings.json", "Web. config" ]},
The new conventions for using folders named "Features" and how folders are organized in them are entirely under your control. By modifying the Viewlocationformats set (or the behavior of the Featureviewlocationexpander type), you have full control over where your app's view is located, which is the only action needed to reorganize your files. Because the controller type is found regardless of the folder in which it is located.
Parallel features Folder
If you want to try the feature folder in parallel with the default MVC area and view conventions, you can do so with minimal modification. Insert the functional format into the starting position of the list instead of clearing the viewlocationformats (note that the order is reversed):
Options. Viewlocationformats.insert (0"/features/shared/{0}.cshtml"); options. Viewlocationformats.insert (0"/features/{3}/{0}.cshtml"); options. Viewlocationformats.insert (0"/features/{3}/{1}/{0}.cshtml");
To support functionality that is combined with a zone, the Areaviewlocationformats collection is also modified:
Options. Areaviewlocationformats.insert (0"/areas/{2}/features/shared/{0}.cshtml" ); options. Areaviewlocationformats.insert (0"/areas/{2}/features/{3}/{0}.cshtml" ); options. Areaviewlocationformats.insert (0"/areas/{2}/features/{3}/{1}/{0}.cshtml ");
How is the model handled?
A keen reader will notice that I did not move my model type into the features folder (or areas). In this example, I don't have a separate ViewModel type, because the model I'm using is extremely simple. In a real-world application, your domain or persistence model may have more complexity than you need for your view, which is defined by itself in a separate project. Your MVC app may define ViewModel types that contain only the data needed for a given view, optimized for display (or used by client API requests). There is no doubt that these ViewModel types should be placed in the functional folders they are used in (it should be uncommon for these types to be shared among functions).
Summarize
Examples include all three versions of the Ninjapirateplantzombie organizer application and support for adding and viewing each type of data. Download the sample (or view it on GitHub) and think about how each method works in the context of the application you are currently using. The experiment adds an area or feature folder to a large application that you use and determines whether you prefer to use the feature breakdown as the top-level organization for your app's folder structure compared to using top-level folders based on file types.
The source code for this sample is available through Https://github.com/smallprogram/OrganizingAspNetCore.
The functional division of ASP. NET Core-asp.net Core MVC