Overview of MVC Projects MVC项目概述

We are going to provide some additional context before we start diving into the details of specific MVC Framework features. This chapter gives an overview of the structure and nature of an ASP.NET MVC application, including the default project structure and naming conventions that you must follow.

Working with Visual Studio MVC Projects 与Visual Studio的MVC项目一起工作

When you create a new MVC3 project, Visual Studio gives you a choice between three starting points: Empty, Internet Application, or Intranet Application, as shown in Figure 10-1.

Figure 10-1. Choosing the initial configuration of an MVC 3 project

The Empty project template is the one we used in Chapter 3 for the RSVP application and in Chapter 7 when we set up for the SportsStore application. It creates a relatively small number of files and directories, and gives you just the minimum structure on which to build.
Empty项目模板是我们第3章的RSVP（电子回复）应用程序、以及第7章在我们建立SportsStore应用程序时所使用的模板。它生成相对较少的文件和目录，并且只为你建立最小化的结构。

n Note The New ASP.NET MVC 3 Project dialog box (Figure 10-1) has a Use HTML5 semantic markup checkbox. Microsoft has started the process of addingHTML5 support to Visual Studio. We have chosen to ignore HTML5 in this book. The MVC Framework is agnostic toward the version of HTML used in a project. HTML5 is a topic in its own right. If you are interested in learning more about HTML5, consider reading Adam’s The Definitive Guide to HTML5, also published by Apress.

The Internet Application and Intranet Application templates fill out the project to give a more complete starting point, using different authentication mechanisms that are suited to Internet and intranet applications(see Chapter 22 for details on both authentication systems).
Internet应用程序和Intranet应用程序模板扩充了项目以达到更复杂的起点，采用不同的认证机制以适应Internet和intranet应用程序（两种认证机制详情请参阅第22章）。

n Tip When using the Internet Application or Intranet Application template, you can also elect to create a unit test project as part of the Visual Studio solution. This is another convenience, since you can also create a test project yourself, as we did in Chapter 7.

You can see the difference between these three startup options in Figure 10-2, which shows the initial project set up for these three templates.

Figure 10-2. The initial configuration of MVC projects created using the Empty, Internet Application, and Intranet Application templates

The templates create projects that have a common structure. Some of the project items have special roles, which are hard-coded into ASP.NET or the MVC Framework. Others are subject to naming conventions. We have described each of these files and folders in Table 10-1.

Table 10-1. Summary ofMVC 3 Project Items

n Note As you’ll see in Chapter 23, an MVC application is deployed by copying the folder structure to your web server. For security reasons, IIS won’t serve files whose full paths contain Web.config, bin, App_code, App_GlobalResources, App_LocalResources, App_WebReferences, App_Data, orApp_Browsers. IIS will also filter out requests for .asax, .ascx, .sitemap, .resx, .mdb, .mdf, .ldf, .csproj, and various other file name extensions. If you do decide to restructure your project, you must be sure not to use these names and extensions in your URLs.

Table 10-2 describes the folders and files that have special meanings if they exist in your MVC 3project.

Table 10-2. Summary of Optional MVC 3 Project Items

 Folder or File 文件或文件夹 Description 描述 /Areas Areas are a way of partitioning a large application into smaller pieces. We’ll explain how areas work in Chapter 11. Areas是把一个大型应用程序划分成较小片段的一种方式。我们在第11章解释区域是如何工作的。 /App_GlobalResources /App_LocalResources These contain resource files used for localizing Web Forms pages. 这些包含了用于定位Web表单页面的资源文件。 /App_Browsers This folder contains .browser XML files that describe how to identify specific web browsers, and what such browsers are capable of (whether they support JavaScript, for example). 此文件夹包含了.browser的XML文件，它描述了如何标识特定的web浏览器，以及这些浏览器具备什么能力（例如，是否支持JavaScript）。 /App_Themes This folder contains Web Forms themes (including .skin files), which influence how Web Forms controls are rendered. 这个文件夹含有Web表单的主题（包括.skin文件），它们影响着如何渲染Web表单控件。

n Note Except for /Areas, the items in Table 10-2 are part of the core ASP.NET platform and are not particularly relevant for MVC applications. Adam goes into detail about the underlying ASP.NET features in his books Applied ASP.NET 4 in Context and Pro ASP.NET 4, both published by Apress.

Using the Internet and Intranet Application Controllers 使用Internet和Intranet应用程序控制器

Figure 10-2 shows that the Internet Application and Intranet Application templates add some default controllers, views, layouts, and view models. Quite a bit of functionality is included, especially in projects created using the Intranet Application template.

The HomeController(present in both the Internet Application and Intranet Application templates) can render a Home page and an About page. These pages are generated with the default layout, which uses a soothing blue-themed CSS file.

The Internet Application template also includes AccountController, which allows visitors to register and log on. It uses forms authentication to keep track of whether you’re logged in, and it uses the core ASP.NET membership facility (which we discuss in Chapter22) to record the list of registered users. The membership facility will try to create a SQL Server Express file-based database on the fly in your /App_Data folder the first time anyone tries to register or log in. This will fail, after a long pause, if you don’t have SQL Server Express installed and running. AccountController also has actions and views that let registered users change their passwords. (The Intranet Application template omits AccountController because accounts and passwords are expected to be managed through a Windows domain/Active Directory infrastructure.)
Internet应用程序模板也包括了AccountController，这允许访问者进行注册和登录。它使用表单认证来跟踪你是否登录，而且它使用核心的ASP.NET成员工具（第22章讨论）来记录已注册用户。这个成员工具将在有人第一次试图注册或登录时，试着在你闲置的/App_Data文件夹中生成一个基于文件的SQL Server Express数据库。如果你未安装和运行SQL Server Express，经过一段较长时间的暂停之后，这一生成过程将会失败。AccountController也有让已注册用户修改其口令的动作和视图。（intranet应用程序模板略去了AccountController，因为账号和口令是期望通过一个Windows域/活动目录体系结构去管理的。）

The default controllers and views can be useful to get your project started, but we tend to use the Empty template so that our application contains only the items that we need.

Understanding MVC Conventions 理解MVC约定

There are two kinds of conventions in an MVC project. The first kind are really just suggestions as to how you might like to structure your project. For example, it is conventional to put your JavaScript files in the Scripts folder. This is where other MVC developers would expect to find them, and where Visual Studio puts the initial JavaScript files for a new MVC project. But you are free to rename the Scripts folder, or remove it entirely, and put your scripts anywhere you like. That wouldn’t prevent the MVC Framework from running your application.

The other kind of convention arises from the principle of convention over configuration, which was one of the main selling points that made Ruby on Rails so popular. Convention over configuration means that you don’t need to explicitly configure associations between controllers and their views. You just follow a certain naming convention, and everything works. There is less flexibility in changing your project structure when dealing with this kind of convention. The following sections explain the conventions that are used in place of configuration.

n Tip All of the conventions can be changed using a custom view engine, which we cover in Chapter 15.

Following Conventions for Controller Classes 遵循控制器类约定

Controller classes must have names that end with Controller, such as ProductController, AdminController, and HomeController.

When referencing a controller from an MVC route or an HTML helper method, you specify the first part of the name (such as Product), and the DefaultControllerFactory class automatically appends Controller to the name and starts looking for the controller class. You can change this behavior by creating your own implementation of the IControllerFactory interface, which we describe in Chapter 14.

Following Conventions for Views 遵循视图约定

Views and partial views should go into the folder /Views/Controllername. For example, a view associated with the ProductController class would go in the /Views/Product folder.

n Note Notice that we omit the Controller part of the class from the Views folder; we use the folder /Views/Product, not /Views/ProductController. This may seem counterintuitive at first, but it quickly becomes second nature.

The MVC Framework expects that the default view for an action method should be named after that method .For example, the view associated with an action method called List should be called List.cshtml (or List.aspx if you are using the legacy ASPX view engine).Thus, for the List action method in the ProductController class, the default view is expected to be /Views/Product/List.cshtml.
MVC框架期望对一个动作方法的默认视图应当按照该方法命名。例如，与名为List动作方法相关联的视图应该被叫做List.cshtml（或List.aspx，如果你用的是遗留的ASPX视图引擎）。这样，对于ProductController中的List动作方法，所期望的默认视图便是/Views/Product/List.cshtml。

The default view is usedwhen you return the result of calling the View method in an action method, likethis:

return View();

You can specify a different view by name, like this:

return View("MyOtherView");

Notice that we don’t include the file name extension or the path to the view. The MVC Framework will try to find the view using the file name extensions for the installed view engines (Razor and the ASPX engine by default).

When looking for a view, the MVC Framework looks in the folder named after the controller and then in the /Views/Shared folder. This means that we can put views that will be used bymore than one controller in the /Views/Shared folder and rely on the framework to find them.

Following Conventions for Layouts 遵循布局约定

The naming convention for layouts is to prefix the file with an underscore (_) character (as we explained in Chapter 9, this originates from WebMatrix, which also uses Razor), and layout files are placed in the /Views/Shared folder. Visual Studio creates a layout called _Layout.cshtml as part of the initial project template. This layout is applied to all views by default, through the /Views/_ViewStart.cshtmlfile, which we discussed in Chapter 5.

If you don’t want the default layout applied to views, you can change the settings in_ViewStart.cshtml (or delete the file entirely) to specify another layout in the view, like this:

@{
Layout ="~/Views/Shared/MyLayout.cshtml";
}

Or you can disable any layout for a given view, like this:

@{
Layout =null;
}

Debugging MVC Applications 调试MVC应用程序

You can debug an ASP.NETMVC application in exactly the same way as you debug an ASP.NET Web Forms application. The Visual Studio debugger is a powerful and flexible tool, with many features and uses. We can only scratch the surface in this book. We will show you how to set up the debugger, create breakpoints, and run the debugger on your application and unit tests.

Creating the Project 生成项目

To demonstrate using the debugger, we have created a new MVC 3 project using the Internet Application template. This gives us the initial controller and views to use. We have called our project DebuggingDemo and checked the option to create a unit test project, as shown in Figure 10-3.

Figure 10-3. Creating the DebuggingDemo project

Launching the Visual Studio Debugger 运行Visual Studio调试器

Before we can debug an MVC application, we should check our configuration in Visual Studio. We want to compile our C# classes (such as controllers and domain model entities) in debug mode. The menu for setting this option is shown in Figure10-4; Debug is the default.

Figure 10-4. Selecting the Debug configuration

The simplest way to start the debugger is to press F5. Alternatively, you can select Start Debugging from the Visual Studio Debug menu. The first time that you start the debugger on an application, you may see the dialog box shown in Figure 10-5.

Figure 10-5. Enabling debugging in an MVC application

If you select the option to modify the Web.config file, the compilation section will be updated so that the value of the debug attribute is true, as shown in Listing 10-1. You can change this value by hand if you prefer.

Listing 10-1. Enabling the Debug Attribute in the Web.config File

<configuration>
...
<system.web>
<compilation debug="true"targetFramework="4.0">
...
</compilation>
</system.web>
</configuration>


n Caution Do not deploy your application to a production server without disabling the debug settings. We explain why this is, and how you can automate this change as part of your deployment process, in Chapter 23.

At this point, your application will be started and displayed in a new browser window. The debugger will be attached to your application, but you won’t notice any difference until the debugger breaks (we explain what this means in the next section). To stop the debugger, select Stop Debugging from the Visual Studio Debug menu.

Causing the Visual Studio Debugger to Break 引发VisualStudio调试器中断

An application that is running with the debugger attached will behave normally until a break occurs, at which point the execution of the application is halted and control is turned over to the debugger. At this point, you can inspect and control the state of the application. Breaks occur for two main reasons: when a breakpoint is reached and when an unhandled exception arises. You’ll see examples of both in the following sections.

n Tip You can manually break the debugger by selecting Break All from the Visual Studio Debug menu while the debugger is running.

Using Breakpoints 使用断点

A breakpoint is an instruction that tells the debugger to halt execution of the application and hand control to the programmer. At this point, you can inspect the state of the application and see what is happening. To demonstrate a breakpoint, we have added some statements to the Index method of the HomeController class, as shown in Listing 10-2.

Listing 10-2. Additional Statements in the HomeController Class

using System.Web.Mvc;

namespace DebuggingDemo.Controllers {
publicclass HomeController : Controller {
publicActionResult Index() {
intfirstVal = 10;
intsecondVal = 5;
intresult = firstVal / secondVal;
ViewBag.Message= "Welcome to ASP.NET MVC!";
returnView(result);
}

return View();
}
}
}


These statements don’t do anything interesting. We’ve included them just so we can demonstrate some o fthe debugger features. For this demonstration, we want to add a breakpoint for the statement that sets a value for the ViewBag.Message property.

To create a breakpoint, right-click a code statement and select Breakpoint → Insert Breakpoint from the pop-up menu. A red dot will appear to the left of the statement, as shown in Figure10-6.

If we start the application with the debugger (by selecting Start Debugging from the Debug menu), the application will run until the statement that has the breakpoint is reached, at which point the debugger will transfer control back to us, as shown in Figure 10-7.

Figure 10-7. Hitting a breakpoint

n Note A breakpoint is triggered only when the statement it is associated with is executed. Our example breakpoint was reached as soon as we started the application because it is inside the action method that is called when a request for the default URL is received. If you place a breakpoint inside another action method, you must use the browser to request a URL associated with that method. This can mean working with the application in the way a user would or navigating directly to the URL in the browser window.

You can place breakpoints where you think there are problems in the application. Once a breakpoint has been reached, you can see the call stack that has led to the currently executed method, view the values of fields and variables, and much more. Figure 10-8shows two ways in which you can see the values of the variables we added to the Index method: using the Locals window and by moving the mouse over the variable in the code window.

Figure 10-8. Viewing variable values in the debugger

You can also control the execution of the application. If you drag the yellow arrow that appears in the breakpoint dot, you can specify the statement that will be executed next. Additionally, you can use the Step Into, Step Over, and Step Out items in the Debug menu.

To remove a breakpoint, right-click the code statement and select Breakpoint → Delete Breakpoint from the pop-up menu. To remove all breakpoints, select Delete All Breakpoints from the Debug menu.

n Tip You can add breakpoints to views in order to debug Razor views. This can be very helpful for inspecting the values of view model properties, for example. You add a breakpoint to a view just as we did in the code file: right-click the Razor statement that you are interested in and select Breakpoint → Insert Breakpoint.

Breaking on Exceptions 异常中断

Unhandled exceptions are a fact of development. One of the reasons that we do a lot of unit and integration testing in our projects is to minimize the likelihood that such an exception will occur in production. The Visual Studio debugger will break automatically when it sees an unhandled exception.

n Note Only unhandled exceptions cause the debugger to break. An exception becomes handled if you catch and handle it in a try...catch block. Handled exceptions are a useful programming tool. They are used to represent the scenario where a method was unable to complete its task and needs to notify its caller. Unhandled exceptions are bad, because they represent an unexpected condition that we didn’t try to compensate for (and because they drop the userinto an error page).

To demonstrate breaking on an exception, we have made a small change to the Index action method, as shown in Listing 10-3.

Listing 10-3. Adding a Statement That Will Cause an Exception

using System.Web.Mvc;

namespace DebuggingDemo.Controllers {
publicclass HomeController : Controller {
publicActionResult Index() {
ViewBag.Message= "Welcome to ASP.NET MVC!";
intfirstVal = 10;
intsecondVal = 0;
intresult = firstVal / secondVal;
returnView(result);
}

returnView();
}
}
}


We changed the value of the secondVal variable to be 0, which will cause an exception in the statement that divides firstVal by secondVal. If you start the debugger, the application will run until the exception is thrown, at which point the exception helper pop-up will appear, as shown in Figure 10-9.

Figure 10-9. The exception helper

The exception helper gives you details of the exception. When the debugger breaks on an exception, you can inspect the application state and control execution, just as when a breakpoint is hit.

Using Edit and Continue 使用编辑并继续

One of the most interesting Visual Studio debugging features is called Edit and Continue. When the debugger breaks, you can edit you code and then continue debugging. Visual Studio recompiles your application and re-creates the state of your application at the moment of the debugger break.

Enabling Edit and Continue 启用编辑并继续

We need to enable Edit and Continue in two places:

l In the Edit and Continue section of the Debugging options (select Options from the Visual Studio Tools menu), make sure that Enable Edit and Continue is checked, as shown in Figure 10-10.

Figure 10-10. Enabling Edit and Continue in the Options dialog box

l In the project properties (select <projectname> Properties from the Visual Studio Project menu), click the Web section and ensure thatEnable Edit and Continue is checked, as shown in Figure 10-11.

Figure 10-11. Enabling Edit and Continue in the project properties

Modifying the Project 修改项目

The Edit and Continue feature is somewhat picky. There are some conditions under which it cannot work. One such condition is present in the Index action method of the HomeController class: the use of dynamic objects. In this case, we are using the View Bag feature to set a message to be displayed by the view. We havereplaced this with a call to the View Data feature instead, as shown in bold in Listing 10-4.
“编辑并继续”特性有点过于讲究。有某些情况下它不能工作。一种情况是HomeController类的Index动作方法中的情况：使用动态对象。在这种情况下，我们在使用View Bag特性来设置由视图显示的消息。我们用一个View Data特性来代替它，如清单10-4的黑体所示。

Listing 10-4. Removing the View Bag Call from the Index Method

public ActionResult Index() {
intfirstVal = 10;
intsecondVal = 0;
intresult = firstVal / secondVal;
ViewData["Message"] ="Welcome to ASP.NET MVC!";
returnView(result);
}


We need to make a corresponding change in the Index.cshtml view, as shown in Listing 10-5.

Listing 10-5. Removing the View Bag Call from the View

@model int
@{
}
<h2>@ViewData["Message"]</h2>
<p>
The calculation result value is: @Model
</p>


We’ve also taken the opportunity to make the view strongly typed and display the result of the calculation performed in the Index method in the output.

Editing and Continuing 编辑并继续

We are ready for a demonstration of the Edit and Continue feature. Begin by selecting Start Debugging from the Visual Studio Debug menu. The application will be started with the debugger attached and run until it reaches the line where we perform a simple calculation in the Index method. The value of the second parameter iszero, which causes an exception to be thrown. At this point, the debugger halts execution, and the exception helper pops up (see Figure 10-9).

Click the Enable editing link in the exception helper window, and change the statement that performs the calculation as shown in Listing 10-6.

Listing 10-6. EditingLive Code

public ActionResult Index() {
intfirstVal = 10;
intsecondVal = 0;
intresult = firstVal / 5;
ViewData["Message"]= "Welcome to ASP.NET MVC!";
returnView(result);
}


Select Continue from the Debug menu. The application will continue to execute, and the browser will display the rendered page, which is shown in Figure 10-12.

Figure 10-12. The effect of Edit and Continue

Take a moment to reflection what happened here. We started the application with a bug in it—an attempt to divide a value by zero. The debugger detected the exception and stopped executing the program. We edited the code to fix the bug, replacing the reference to the variable with the literal value 5. We then told the debugger to continue the execution.

At this point, Visual Studio recompiled our application so that our change was included in the build process, restarted execution, re-created the state that led to the exception,and then carried on as normal. The browser received the rendered result, which reflected our correction.

Without Edit and Continue, we would have needed to stop the application, make our changes, compile the application, and restart the debugger. We would then use the browser to repeat the steps that we took up to the moment of the debugger break. It is avoiding this last step that can be the most important. Complex bugs may require many steps through the application to re-create, and the ability to test potential fixes without needing to repeat those steps over and over can save the programmer’s time and sanity.

DEBUGGING UNIT TESTS

We can use the Visual Studio debugger to debug unit tests. We do this by selecting one of the options in the Test → Debug menu. This is like running the unit tests, but with the debugger attached. That may seem like an odd thing to do, but there are two situations in which this is a useful feature:

• When you are getting unexpected or inconsistent behavior from a unit test. When this happens, you can use breakpoints to interrupt execution of the unit test and see what is going on.
当你得到一个单元测试的异常或不一致行为时。出现这种情况时，你可以用断点来中断这个单元测试的执行，并看看是怎么回事。
• When you want to know more about the state of a test when it fails. This is the situation we found ourselves facing most frequently. The Assert methods we use to check the result of a unit test throw an exception if the specific condition isn’t true, and since the debugger breaks when it encounters an unhandled exception, we can debug our unit test, wait until it fails, and then see what was happening up until the moment the assertion failed.
当你在单元测试失败时，想更多地了解该单元测试的状态。这是我们最经常要面对的情况。如果我们所指定的条件不为真，我们用来检查单元测试结果的Assert方法会弹出一个异常，而从调试器遇到一个未处理异常而中断开始，我们可以调试我们的单元测试、等待直到它失败、然后看看直到断言失败时都发生了些什么。

Project-Wide Dependency Injection 项目范围的依赖性注入

In the chapters that follow, you’ll see just how many different ways the MVC Framework provides fo ryou to extend or customize how requests are serviced, each of which is defined by an interface to implement or a base class from which to derive.

You have already seen one example of customizing the MVC Framework when we developed the SportsStore application. We derived from the DefaultControllerFactory class to create NinjectControllerFactory so that we could create controllers using Ninject to manage DI. If we followed this approach for each of the points of customization in the MVC Framework, we would end up being able to use DI throughout an application, but we would have a lot of code duplication and more Ninject kernels than we would ideally like.

When the MVC Framework needs to create an instance of a class, it calls the static methods of theSystem.Web.Mvc.DependencyResolver class. We can add DI throughout an MVC application by implementing the IDependencyResolver interface and registering our implementation with DependencyResolver. That way, whenever the framework needs to create an instance of a class, it will call our class, and we can call Ninject to create the object.

We didn’t consolidate our DI during the development of SportsStore because we just wanted to demonstrate adding DI to controllers. Listing 10-7 shows how we can implement the IDependencyResolver interface for that application.

Listing 10-7. The NinjectIDependencyResolver Implementation

using System;
using System.Collections.Generic;
using System.Web.Mvc;
using Ninject;
using Ninject.Parameters;
using Ninject.Syntax;
using SportsStore.Domain.Abstract;
using SportsStore.Domain.Concrete;
using SportsStore.WebUI.Infrastructure.Abstract;
using SportsStore.WebUI.Infrastructure.Concrete;
using System.Configuration;

namespace SportsStore.WebUI.Infrastructure {
publicclass NinjectDependencyResolver : IDependencyResolver {
privateIKernel kernel;
publicNinjectDependencyResolver() {
kernel= new StandardKernel();
}

publicobject GetService(Type serviceType) {
return kernel.TryGet(serviceType);
}

publicIEnumerable<object> GetServices(Type serviceType) {
returnkernel.GetAll(serviceType);
}

publicIBindingToSyntax<T> Bind<T>() {
returnkernel.Bind<T>();
}

publicIKernel Kernel {
get{ return kernel; }
}

Bind<IProductRepository>().To<EFProductRepository>();
Bind<IAuthProvider>().To<FormsAuthProvider>();

//create the email settings object
EmailSettingsemailSettings = new EmailSettings {
WriteAsFile = bool.Parse(
ConfigurationManager.AppSettings["Email.WriteAsFile"] ??"false")
};

Bind<IOrderProcessor>()
.To<EmailOrderProcessor>()
.WithConstructorArgument("settings", emailSettings);
}
}
}


This class is simpler than it looks. The first two methods are called by the MVC Framework when it requires a new instance of a class, and we simply call the Ninject kernel to pass along the request. We have added the Bind method so that we can add bindings from outside this class. This is strictly optional because we have also included the AddBindings method, which is called from the constructor, just as we did in the NinjectControllerFactory class in Chapter 7.

We can now delete the NinjectControllerFactory class and register the more general NinjectDependencyResolver class in the Application_Start method of Global.asax,as shown in Listing 10-8.

Listing 10-8. Registering the IDependencyResolver Implementation

protected void Application_Start() {
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);

DependencyResolver.SetResolver(newNinjectDependencyResolver());
}


With these changes, we have put Ninject at the heart of the MVC application. We can still take advantage of the extension points in the MVC Framework, but we no longer need to if all we want to do is introduce DI into some part of the request pipeline.

Summary 概要

In this chapter, we have shown you the structure of a Visual Studio MVC 3 project and how the various parts fit together. We have also touched on two of the most important characteristics of the MVC Framework: convention and extensibility. These are topics that we will return to again and again in the chapters that follow, as we dig deeper into how the MVC Framework operates.

• 广告
• 抄袭
• 版权
• 政治
• 色情
• 无意义
• 其他

120