Demystifying the ASP.NET MVC Project Type

Good Post from Kevin. simply sharing as it is

Most people interested in ASP.NET MVC have at least looked through the Nerd Dinner demo project from Wrox’s Professional ASP.NET MVC 1.0 book. While it's likely that new projects will start using the ASP.NET MVC Web Application Visual Studio project template just as Nerd Dinner does, I like to know what exactly those templates are doing. This knowledge can come in handy when you want to transition legacy applications to MVC or (perhaps more realistically) use MVC for some capabilities in an existing Web Forms application, such as a RESTful API.
This post outlines how to take a standard ASP.NET Web Application project and "transform" it into an MVC project.

Getting Started

I'm starting with a completely stock ASP.NET Web Application project template on a machine that ASP.NET MVC v1.0 was installed on.
New project template using an ASP.NET Web Application templateOnce we’ve created a new project, the first thing to do is add references to the MVC framework and its supporting libraries which include System.Web.Abstractions, System.Web.Mvc and System.Web.Routing.
02_referencesWith the assembly references in place, it's now time to tell Visual Studio that this project is MVC-aware. To do so, close the project within VS, open the corresponding .csproj file in a text editor and add the MVC project type GUID to the <ProjectTypeGuids> element, save and close the file.
Add the MVC Project Type GUID (which starts with 603c0e0b) to the .csproj file so that it looks like this (line-breaks added for the post only):
1.<ProjectTypeGuids>
2.   {603c0e0b-db56-11dc-be95-000d561079b0};
3.   {349c5851-65df-11da-9384-00065b846f21};
4.   {fae04ec0-301f-11d3-bf4b-00c04f79efbc}
5.</ProjectTypeGuids>
With that change completed, re-open the project and VS will now offer MVC-based Master Pages, Views, User Controls and Controller templates when you add new items.
03_mvc-templates
Let's also open up the Default.aspx file added by the VS template and add the following HTML between the <div> tags so we know what page we're looking at when we run the project later:
1.<h1>Hello, from the Default.aspx Web Form!</h1></CODE>

Configuring Routes

Next up, we'll add a Global.asax file to the project, so we can configure routes. This is done normally, by right-clicking on the project name, selecting Add New Item and picking a Global Application Class under the Web category. To make our changes match the MVC project templates, we'll remove all the stub methods the template added for us, except for the Application_Start method, which we'll modify to call the new RegisterRoutes method that we'll also add. For this example, we'll just add the default routes the MVC template includes, but this is the spot you'll customize your routes just like you would in another MVC project. This is what the final code should look like for the Global application class:
01.using System;
02.using System.Web.Mvc;
03.using System.Web.Routing;
04.  
05.namespace DemystifyingMvc
06.{
07.    public class Global : System.Web.HttpApplication
08.    {
09.        public static void RegisterRoutes(RouteCollection routes)
10.        {
11.            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
12.  
13.            routes.MapRoute(
14.                "Default",                                              
15.                "{controller}/{action}/{id}",                           
16.                new { controller = "Home", action = "Index", id = "" }  
17.            );
18.  
19.        }
20.  
21.        protected void Application_Start(object sender, EventArgs e)
22.        {
23.            RegisterRoutes(RouteTable.Routes);
24.        }
25.    }
26.}

Adding Controllers & Views

Now it's time to add a controller and view and wire it all together. We'll add a Controllers and Views folder to the project, like so:
04_add-folders There's nothing special about these folders, they are added using Add > New Folder, but MVC's conventions will search them so it's where we'll place our controllers and views. To make the example line up with the default route, we'll now add a HomeController with an Index action by right-clicking on the Controllers folder and selecting Add > Controller (this 05_add-viewoption is available because of the Project Type GUID we added earlier). The default code added by the template works for this example, so we'll leave it and move on to the view.
To add views for the HomeController we'll add a Home folder under the Views one and then right-click on the Home folder to select Add > View. The Add View dialog will display, and we'll name the view Index, and avoid using a Master Page for this example.
Next, add the following HTML inside the <div> tags in the template so that we can tell when everything is working:
1.<h1>Hello, from the Home/Index view!</h1>
You may notice that the Mvc in the inherits statement of the new view is highlighted by VS, with a tooltip stating "Cannot resolve symbol 'Mvc'". This is because we haven't done any of the configuration steps handled by the MVC project template yet. Let's do that next.

XML Configuration

First, open up the Web.config file in the root of the project so we can fix the assembly reference issues in the view. Under the system.web/compilation/assemblies node, we'll add the System.Web.Abstractions, System.Web.Routing and System.Web.Mvc assemblies so that it looks like this:
01.<assemblies>
02.   <add assembly="System.Core, 
03.      Version=3.5.0.0, Culture=neutral
04.      PublicKeyToken=B77A5C561934E089"/>
05.   <add assembly="System.Data.DataSetExtensions, 
06.      Version=3.5.0.0, Culture=neutral
07.      PublicKeyToken=B77A5C561934E089"/>
08.   <add assembly="System.Web.Extensions, 
09.      Version=3.5.0.0, Culture=neutral,
10.      PublicKeyToken=31BF3856AD364E35"/>
11.   <add assembly="System.Xml.Linq, 
12.      Version=3.5.0.0, Culture=neutral,
13.      PublicKeyToken=B77A5C561934E089"/>
14.   <add assembly="System.Web.Abstractions, 
15.      Version=3.5.0.0, Culture=neutral
16.      PublicKeyToken=31BF3856AD364E35"/>
17.   <add assembly="System.Web.Routing,
18.      Version=3.5.0.0, Culture=neutral,
19.      PublicKeyToken=31BF3856AD364E35"/>
20.   <add assembly="System.Web.Mvc, 
21.      Version=1.0.0.0, Culture=neutral,
22.      PublicKeyToken=31BF3856AD364E35"/>
23.</assemblies>
ASP.NET MVC uses HTTP Modules and Handlers to perform its work, so we'll need to add these to the Web.config file as well. For IIS6 and 7 support, we'll add it to the following three locations.
First up, add the following XML to the system.web/httpModules node:
1.<add name="UrlRoutingModule" 
2.   type="System.Web.Routing.UrlRoutingModule, 
3.      System.Web.Routing, Version=3.5.0.0, Culture=neutral
4.      PublicKeyToken=31BF3856AD364E35"/>
Secondly, add the following XML to the system.webServer/modules node:
1.<remove name="UrlRoutingModule"/>
2.<add name="UrlRoutingModule" 
3.   type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, 
4.      Version=3.5.0.0, Culture=neutral
5.      PublicKeyToken=31BF3856AD364E35"/>
Lastly, add the following XML to the system.webServer/handlers node:
1.<remove name="UrlRoutingHandler"/>
2.<add name="UrlRoutingHandler" preCondition="integratedMode" 
3.   verb="*" path="UrlRouting.axd" 
4.   type="System.Web.HttpForbiddenHandler, System.Web, 
5.      Version=2.0.0.0, Culture=neutral
6.      PublicKeyToken=b03f5f7f11d50a3a"/>
In order to get strongly-typed views working we also need to add a new Web.config file to the Views folder. To do so, right-click on the Views folder and select Add > New Item then select the Web Configuration File template from the Web category. Replace the content of the new Web.config file with the following XML which comes right out of the MVC project template:
01.<?xml version="1.0"?>
02.<configuration>  
03.  <system.web>
04.    <httpHandlers>
05.      <add path="*" verb="*"
06.          type="System.Web.HttpNotFoundHandler"/>
07.    </httpHandlers>
08.  
09.    <!--
10.        Enabling request validation in view pages would cause validation to occur
11.        after the input has already been processed by the controller. By default
12.        MVC performs request validation before a controller processes the input.
13.        To change this behavior apply the ValidateInputAttribute to a
14.        controller or action.
15.    -->
16.    <pages
17.        validateRequest="false"
18.        pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, 
19.           System.Web.Mvc, Version=1.0.0.0, Culture=neutral
20.           PublicKeyToken=31BF3856AD364E35"
21.        pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, 
22.           Version=1.0.0.0, Culture=neutral
23.           PublicKeyToken=31BF3856AD364E35"
24.        userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc,
25.           Version=1.0.0.0, Culture=neutral
26.           PublicKeyToken=31BF3856AD364E35">
27.      <controls>
28.        <add assembly="System.Web.Mvc, Version=1.0.0.0, 
29.           Culture=neutral, PublicKeyToken=31BF3856AD364E35
30.           namespace="System.Web.Mvc" 
31.           tagPrefix="mvc" />
32.      </controls>
33.    </pages>
34.  </system.web>
35.  
36.  <system.webServer>
37.    <validation validateIntegratedModeConfiguration="false"/>
38.    <handlers>
39.      <remove name="BlockViewHandler"/>
40.      <add name="BlockViewHandler" 
41.         path="*" verb="*" 
42.         preCondition="integratedMode" 
43.         type="System.Web.HttpNotFoundHandler"/>
44.    </handlers>
45.  </system.webServer>
46.</configuration>
This sets up some default handlers so that it's not possible to access your views using their file locations (for example /views/home/index.aspx). More importantly, it configures ASP.NET to use the MVC framework's ViewTypeParserFilter, without which strongly-typed MVC views will not work.

Test Run

With all that wonderful XML in place, things should be wired up properly, so now it's time to run the application for the first time. You should be able to build and launch it, but you may be surprised to see the content from the Default.aspx page shown, and not that of the HomeController's Index view. I'll explain this in a moment.
06_run1 It is, however, possible to navigate to /home or /home/index to see the content from the Views/Home/Index.aspx view:
07_run2From this, we can see that MVC is configured and servicing requests. If we want requests to the root of the application to also be handled by MVC, we'll have to make one last change.

MVC the Root

If you want MVC to handle requests to the root of your application (the / path) we need to make some changes to the Default.aspx page so that it matches the setup in the MVC project template. Before we do this, you can now see that having Web Forms and MVC in the same project is pretty straightforward. In fact, this is how the project is setup right now. For instance, we could configure MVC to only handle requests to an /api path and implement a RESTful API using MVC, even if the rest of the application is using Web Forms for the user interface.
That said, if we want MVC to handle requests to the application root, we'll modify the code-behind of the Default.aspx file, so that it looks like this:
01.using System;
02.using System.Web;
03.using System.Web.Mvc;
04.  
05.namespace DemystifyingMvc
06.{
07.    public partial class _Default : System.Web.UI.Page
08.    {
09.        protected void Page_Load(object sender, EventArgs e)
10.        {
11.            var originalPath = Request.Path;
12.            HttpContext.Current.RewritePath(Request.ApplicationPath, false);
13.            IHttpHandler httpHandler = new MvcHttpHandler();
14.            httpHandler.ProcessRequest(HttpContext.Current);
15.            HttpContext.Current.RewritePath(originalPath, false);
16.        }
17.    }
18.}
This code manually invokes the MvcHttpHandler for the application path (since the Request.Path contains “/Default.aspx” when the code runs). This manual execution of the MvcHttpHandler allows our default controller and action to get used instead of the Default.aspx file that IIS executes.
To make it clear what we're doing, you may also want to change the mark-up of the Default.aspx page to match what is in the MVC project template. It helps clarify the page's purpose to developers who may not be familiar with MVC.
1.<%@ Page Language="C#" 
2.   AutoEventWireup="true" 
3.   CodeBehind="Default.aspx.cs" 
4.   Inherits="DemystifyingMvc._Default" %>
5.  
6.<%-- Please do not delete this file. It is used to 
7.     ensure that ASP.NET MVC is activated by IIS when a 
8.     user makes a "/" request to the server. --%>
Save our changes, build and re-run the application. You should now see the content from the Home/Index view when accessing the application root.
08_run3 

What about models?

As you can see, we've now got a fully functioning MVC application, even though we don't have any models. That's because there's nothing special about models in the MVC world, they are just objects and don't have to be in a Models folder, or even present at all if you're writing an exceedingly simple application. Obviously, if your app does much of anything useful, you're going to need them, but they can be put where you'd like, such as in a separate assembly where they can be re-used without any dependency on ASP.NET at all.

Summary

And that, my friends, is the detail that the ASP.NET MVC Web Application project template takes care of for you. It should be noted that there are a few configuration settings used in the MVC template that I haven't replicated here, such as handlers that support *.mvc file paths and even some default page namespace inclusions. I've left these out to show that they aren't necessarily required for MVC to function.
Here's a quick recap on what you need to add to a standard ASP.NET Web Application project to get it to function as an MVC project:
  1. Add MVC-related references (System.Web.Abstractions, System.Web.Mvc, System.Web.Routing)
  2. Add the MVC Project Type GUID to your .csproj file to get VS support for MVC
  3. Add a Global.asax and call the RegisterRoutes method from Application_Start
  4. Add a controller and view
  5. Add the MVC assemblies to the system.web/compilation node in the root Web.config
  6. Add the MVC handlers and modules to the root Web.config
  7. Add a Web.config file to the Views folder that uses System.Web.Mvc.ViewTypeParserFilter for strongly-typed View support
  8. (Optional) Modify the Default.aspx code-behind to invoke the MvcHttpHandler so that requests to the root of your application are handled by MVC

Private Static Method in a Class

 
When I'm writing a class, most methods fall into two categories:
  • Methods that change the current instance's state.
  • Helper methods that don't change the current object's state, but help me compute values I need elsewhere.
Static methods are useful, because just by looking at its signature, you know that the calling it doesn't use or modify the current instance's state.
Take this example:
public class Library
{
    private static Book findBook(List<Book> books, string title)
    {
        // code goes here
    }
}
If an instance of library's state ever gets screwed up, and I'm trying to figure out why, I can rule out findBook as the culprit, just from its signature.
I try to communicate as much as I can with a method or function's signature, and this is an excellent way to do that.

Wrapper Class to handle Session in ASP.Net


Public Class MySession

    'private constructor
    Private Sub New()
        Dim i As Integer
        i = 1
    End Sub

    Public Shared ReadOnly Property Current As MySession
        Get
            Dim session As MySession = HttpContext.Current.Session("MySession")
            If (session Is Nothing) Then
                session = New MySession()
                HttpContext.Current.Session("MySession") = session
            End If
            Return session
        End Get
    End Property

    Public Property EmployeeID() As String
        Get
            Return HttpContext.Current.Session("EmployeeID").ToString
        End Get
        Set(ByVal value As String)
            HttpContext.Current.Session("EmployeeID") = value
        End Set
    End Property

End Class

Model View Presenter (MVP)


Model View Presenter (MVP) design pattern is the evolution of the MVC design pattern and it's aimed at providing a cleaner separation of concerns between the view, the model, and the controller improving the architecture (you can use several UI technologies without recompiling the business logic components) and testability of the enterprise solution. The pattern was originally developed at Taligent in the 1990s.

In this article, I will explain the principles and how to use the MVP design pattern along with data binding with an example using C# language in Microsoft.NET.

The Model View Presenter (MVP) design pattern

Starting from the Model View Controller (MVC) design pattern, creators of MVP neatly separated the model from the view/controller pair. The core of MVP is the strictly regulated interaction taking place between the view and the controller. In MVP, this controller is renamed to presenter.
In the Figure 1, you can see an overview of the MVP design pattern.

image1.gif

Figure 1

As you can see in the Figure 1, the view and model are neatly separated (they don't know each other) and the view exposes an interface for the communication of the presenter with the view and in this way the presenter doesn't know the underlying UI technology and the view is mockable for testing purposes. As well as the presenter knows the model and can invoke its operations.

Note that each Presenter object has exactly one reference to a View object, and each View object has exactly one reference to a Presenter object. There is one Model object for the whole application. As you can see it's required to define an interface and a presenter for every View object of your application (each Web form in ASP.NET and form in Windows).

Let's explain the MVP triad at a glance.

The Model represents the business objects of the domain problem. That is business entities encapsulating the data, business services implementing the business logic and rules of your solution. It's a domain object and should have no knowledge of the user interface, unlike previous design patterns such as MVC (Model-View-Controller) where the model implements the user interface to manipulate it which is a big design error if you try to separate the concerns of the domain.

The View is responsible to display the content of the model and interact with the user's events regarding the UI technology of the solution.
The Presenter is responsible to interpret the user's events and the communication with the model objects. It implements the main workflow of the application. This is where the heart of the application lies.

It's remarkable to say that Martin Fowler has extended the original MVP design pattern with Passive View and Supervising Controller/Presenter.
In the case of the Passive View pattern, the main idea is to have no dependencies between the View and the Model concerns and the view is dumb. The Supervising Controller is a flexible interpretation of the original MVP design pattern and the view is not completely dumb and has some responsibilities.

Now well, my approach, to architect an enterprise-class application for being implemented using Microsoft technologies, is to separate concerns in multiple layers and follow the MVP design patter along with data binding (data binding using BindingSource support in Windows applications and data binding with ObjectDataSource support in ASP.NET applications). The View has some responsibilities such as displaying, formatting and gathering data as well as handling user interaction's events. The Presenter has the responsibility to interpret the messages sent and received from the View as well as to invoke the underlying business objects in the business logic layer in the Model. Now, the communication between the layers of the application is through Data Transfer Objects (DTO). The use of DTO is trade-off because we create new DTO, we're increasing the cost of the complexity of the solution and this cost is justified in large enterprise applications. For small and medium application, it makes sense to reference the business objects directly from the View concern (and bind the data represented by the business object in the controls) in order to avoid the definition of new DTO. The idea is to use the data model represented by the business object as the DTO. But it's not correct to invoke operations and business logic directly from the View (data access methods, calculation, invocation of external services, etc).

The solution, which I will explain in this article, will be based on the MVP design pattern along with data binding for small enterprise application.

Implementing the Model View Presenter (MVP) design pattern

Now let's go and implement the principles and concepts of the Model View Presenter (MVP) design pattern along with data binding in a multi-layer enterprise application using C# in Microsoft.NET.

Let's open the Visual Studio.NET 2008 IDE and create an example solution (see Figure 2).

image2.gif

Figure 2

For this example, we're going to create an application which enables managing the contacts of one enterprise. The data source is the Person.Contact table in the AdventureWorks database shipped with SQL Server 2005.
Let's add the business logic layer using a Class Library project (see Figure 3).

image3.gif

Figure 3

Next step is to add the business entities and objects which model your problem domain (see Listing 1). For the definition of the business entities we're going to use strongly typed Data Set and for the data access code we're going to rely on the underlying table adapters.

Let's add a DataSet to the project named DSContactManager as shown in the Figure 4.

image4.gif

Figure 4

Let's add a Contact data table and its underlying table adapter (see Figure 5).

image5.gif

Figure 5

Now let's add the business service objects to implement the business logic concerning the Contact business entity (see Figure 6).

image6.gif

Figure 6

The code definition for the business services regarding the contact entity is the Listing 1.
using System;using System.Collections.Generic;using System.Linq;using System.Text;
namespace BusinessLogicPkg
{
    public class ContactBizServices    {
        private DSContactManager m_dsContact;
        public ContactBizServices(DSContactManager dsContact)
        {
            this.m_dsContact = dsContact;
        }
        public void Load()
        {
            DSContactManagerTableAdapters.ContactTableAdapter taContact = newBusinessLogicPkg.DSContactManagerTableAdapters.ContactTableAdapter();
            taContact.Fill(this.m_dsContact.Contact);
        }
        public void Save()
        {
            DSContactManagerTableAdapters.ContactTableAdapter taContact = newBusinessLogicPkg.DSContactManagerTableAdapters.ContactTableAdapter();
            taContact.Update(this.m_dsContact.Contact);
        }
    }
}
Listing 1

Now let's add the Presentation layer where the presenter objects lies using a Class Library project (see Figure 7).

image7.gif

Figure 7

We need to add a reference to the business logic layer in the PresentationLayer as in the Figure 8.

image8.gif

Figure 8

Next step is to add the ContactManagerPresenter class to handle the workflow for the communication between the UI controls and the model objects as well as to add the IContactManagerView interface to define the interface to be implemented by the underlying UI artifacts which need to communicate with the Presenter artifact.

In order to define the IContactManagerView interface, we deal with the user stories and requirements and in this case, we want to load contact data, to be changed and then save the changes. We need to access to the data model (the strongly typed Data Set DSContactManager) as the DTO and send a message to the user notifying the status of the performed operation (see Listing 2).
using System;using System.Collections.Generic;using System.Linq;using System.Text;using BusinessLogicPkg;
namespace PresentationLayerPkg
{
    public interface IContactManagerView    {
        DSContactManager ContactManager { get; }
        void SendMessageInfo(string strMessage);
    }
}
Listing 2

Now let's define the workflow of the presenter object as shown in Listing 3.
using System;using System.Collections.Generic;using System.Linq;using System.Text;using BusinessLogicPkg;
namespace PresentationLayerPkg
{
    public class ContactManagerPresenter    {
        private IContactManagerView m_objContactManagerView = null;
        public ContactManagerPresenter(IContactManagerView objContactManagerView)
        {
            this.m_objContactManagerView = objContactManagerView;
        }
        public void LoadContact()
        {
            ContactBizServices objContactBizServices = newContactBizServices(this.m_objContactManagerView.ContactManager);
            objContactBizServices.Load();
            this.m_objContactManagerView.SendMessageInfo("Operation has successfully ended");
        }
        public void SaveContact()
        {
            ContactBizServices objContactBizServices = newContactBizServices(this.m_objContactManagerView.ContactManager);
            objContactBizServices.Save();
            this.m_objContactManagerView.SendMessageInfo("Operation has successfully ended");
        }
    }}
Listing 3

Now let's go to the Windows Forms project to define the user interface and communication with the Presenter artifact.

Fist step is to add a reference to the PresentationLayerPkg library as shown in Figure 9.

image9.gif

Figure 9

We need to a reference to the BusinessLogicPkg library as well because we're going to use the data model as our Data Transfer Object (DTO) (see Figure 10).

image10.gif

Figure 10

Now let's add a data source object. Open the Data Sources window and click on the Add New Data Source link to open the Data Source Configuration Wizard. Then select the DSContactManager object in the BusinessLogicPkg library (see Figure 11).

image11.gif

Figure 11

Then drag and drop the defined data source onto the form as a grid control. This action adds a DataGrid control and a binding navigator control in the form. Delete the save icon on the binding navigator to do the load and save actions manually.

Next step is to drag and drop two buttons on the form (one to load the data and the other to save the date after changes) and the underlying event handlers.

Next step is to implement the view interface defined in the presenter library. We need to return an instance of the DSContactManager and show an information message (see Listing 4).
#region IContactManagerView Members
        public BusinessLogicPkg.DSContactManager ContactManager
        {
            get
            {
                return this.dSContactManager;
            }
        }
        public void SendMessageInfo(string strMessage)
        {
            System.Windows.Forms.MessageBox.Show(strMessage, "Information Message"MessageBoxButtons.OK,MessageBoxIcon.Information);
        }
        #endregion
Listing 4

And the implementation of the event handlers is done by invoking the underlying methods in the presenter artifact (see Listing 5).
private void btnLoad_Click(object sender, EventArgs e)
        {
            ContactManagerPresenter objContactManagerPresenter = new ContactManagerPresenter(this);
            objContactManagerPresenter.LoadContact();
        }
        private void btnSave_Click(object sender, EventArgs e)
        {
            ContactManagerPresenter objContactManagerPresenter = new ContactManagerPresenter(this);
            objContactManagerPresenter.SaveContact();
        }

Listing 5

The complete code for the Windows forms is shown in the Listing 6.
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;using PresentationLayerPkg;
namespace WinMVPDatabinding
{
    public partial class Form1 : FormIContactManagerView    {
        public Form1()
        {
            InitializeComponent();
        }
        private void btnLoad_Click(object sender, EventArgs e)
        {
            ContactManagerPresenter objContactManagerPresenter = new ContactManagerPresenter(this);
            objContactManagerPresenter.LoadContact();
        }
        private void btnSave_Click(object sender, EventArgs e)
        {
            ContactManagerPresenter objContactManagerPresenter = new ContactManagerPresenter(this);
            objContactManagerPresenter.SaveContact();
        }
        #region IContactManagerView Members
        public BusinessLogicPkg.DSContactManager ContactManager
        {
            get
            {
                return this.dSContactManager;
            }
        }
        public void SendMessageInfo(string strMessage)
        {
            System.Windows.Forms.MessageBox.Show(strMessage, "Information Message"MessageBoxButtons.OK,MessageBoxIcon.Information);
        }
        #endregion    }
}


Listing 6

The Figure 12 shows when you run the application.

image12.gif

Figure 12