Abstract: There are many approaches available for developing a web application in .NET. In this article, I will give an overview of frameworks that you can choose for your .NET projects.

Current state of web development for .NET developers

Model-view-controller (MVC) application architecture

The default choice for a .NET developer today is probably ASP.NET Core or ASP.NET MVC.

Both frameworks implement the model-view-controller (MVC) architectural pattern. As the name implies, applications following this pattern consist of three main types of building blocks:

Models are a representation of the application state. They encapsulate the business logic necessary to retrieve the state from and persist it back to the data store (often a relational database), as well as to perform the operations modifying the state.

are a representation of the application state. They encapsulate the business logic necessary to retrieve the state from and persist it back to the data store (often a relational database), as well as to perform the operations modifying the state. Controllers respond to all user requests. Their main role is to map the request to the underlying models which implement the business logic, select the correct view for presenting the requested data to the user, and pass it the model with that data.

respond to all user requests. Their main role is to map the request to the underlying models which implement the business logic, select the correct view for presenting the requested data to the user, and pass it the model with that data. Views are responsible for rendering the resulting web page to the user. They read all the necessary data from the model passed to them. To avoid too much logic in a view, the controller can instead pass a specialized view model which serves as a wrapper or data transfer object (DTO) for the model data.

The pattern prescribes how the different building blocks can interact. A controller interacts with both models and views, a view only interacts with models, and a model interacts neither with controllers nor with views.

Figure 1: Interaction between the model, the view and the controller

The MVC architectural pattern is often used in web development frameworks for a reason. It works well with the stateless model of the HTTP protocol:

The HTTP request received by the web server is routed to the controller with all the required information to perform the requested action.

The controller interacts with the model and finally invokes the view with the required data.

The output generated by the view is sent back by the web server as the HTTP response.

Figure 2: HTTP request handling with the MVC pattern

The MVC application architecture is most suitable for development of web applications that benefit from being fully rendered on the server, i.e. those that need to be easily indexable by search engines and don’t require a lot of quick user interaction. Examples of such applications are publicly accessible web portals and dynamic web sites.

ASP.NET Core

Of the two MVC frameworks for .NET, ASP.NET Core is the modern one. It was released as part of.NET Core which means that it can run on Windows as well as on Linux and macOS. It is the recommended framework for development of new web applications following the MVC architectural pattern.

In Visual Studio 2019, a new project can be created using the ASP.NET Core Web Application template. The recommended choice in the second step of the project creation wizard is Web Application (Model-View-Controller) which will generate a simple sample app. If you’re using Visual Studio Code, you can create an equivalent new project from the command line with the following command:

dotnet new mvc

Each building block type has its own folder in the project structure.

- Controller classes are placed in the Controllers folder. They derive from the Controller base class. Their methods are called action methods and serve as entry points handling the requests:

public class HomeController : Controller { public IActionResult Error() { return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); } }

- The Views folder contains views. By convention, they are placed in a subfolder matching the name of the controller which will be using them. Their name matches the action methods they will be used in. Hence, the Views/Home/Index.cshtml file would contain the default view for the HomeController.Index action method. The .cshtml extension indicates that the views are using Razor syntax which combines regular HTML with additional Razor markup which starts with the @ symbol:

@model ErrorViewModel @{ ViewData["Title"] = "Error"; } <h1 class="text-danger">Error.</h1> <h2 class="text-danger">An error occurred while processing your request.</h2> @if (Model.ShowRequestId) { <p> <strong>Request ID:</strong> <code>@Model.RequestId</code> </p> }

- The classes in the Models folder fit the definition of view models. They are typically instantiated in action methods and are usually wrappers or data transfer objects with minimal business logic.

public class ErrorViewModel { public string RequestId { get; set; } public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); }

Most of the business logic in an ASP.NET Core application is typically implemented in services which can be registered in the ConfigureServices method of the Startup class. This way they can be injected in the controller classes which need them using the built-in dependency injection functionality.

By default, requests invoke a specific action method based on the routing convention specified in the Configure method of the Startup class:

app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); });

The first URL component determines the controller ( HomeController if not specified), the second URL component determines the action method in that controller ( Index if not specified), and the third URL component is passed into the action method as a parameter named id . Of course, the routing configuration can be customized as needed.

ASP.NET Core applications are highly extensible.

Internally, each incoming request is processed by a pipeline of components which can do some work before passing on the request to the next component in the pipeline and after receiving the response back from it.

Figure 3: ASP.NET Core request pipeline

The components in the pipeline are named middleware. Many of them are built into the framework and provide what one could consider standard web application functionalities like routing, serving static files, authentication, exception handling, HTTPS redirection, CORS (Cross-Origin Resource Sharing) configuration, etc. The framework makes it easy to develop custom middleware as well.

The current version of ASP.NET Core (3.0) is not limited to running on top of .NET Core.

It can also target .NET framework versions that implement .NET Standard 2.0 (.NET framework 4.7.1 or later is recommended). This can be useful if the application depends on Windows-specific .NET framework libraries which don’t run in .NET Core.

It comes at a price, however.

Such an application will only run in IIS (Internet Information Services web server) on Windows and will not be as performant as .NET Core hosted applications. Also, the upcoming ASP.NET Core 3.0 will run only on .NET Core and won’t support the .NET framework anymore.

Editorial Update: .NET Core 3 and ASP.NET Core 3 have been released. Read more here www.dotnetcurry.com/dotnetcore/1513/whats-new-features-dotnetcore-3

All of this makes targeting the .NET framework only as a temporary solution until the old Windows dependencies can be replaced with .NET Core based alternatives. With the upcoming improvements to interop in .NET Core 3.0, some of these dependencies might even become useable directly from .NET Core if you decide to host your app on Windows.

ASP.NET MVC

ASP.NET MVC is the predecessor of ASP.NET Core.

It runs on top of the .NET framework (with no support for .NET Core) and is not actively developed anymore. Applications written in ASP.NET MVC must be hosted in IIS on Windows. ASP.NET MVC should usually not be used for creating new web applications anymore. ASP.NET Core should be used instead (on top of .NET Core if possible, or .NET framework if necessary).

On the surface, ASP.NET MVC appears similar to ASP.NET Core. The project structure contains the same three core folders:

Controllers folder with controller classes derived from the Controller base class which implement action methods.

folder with controller classes derived from the base class which implement action methods. Views folder with views which use Razor syntax.

folder with views which use Razor syntax. Models folder with model (or view model) classes.

However, there is only limited built-in support for dependency injection with no implementation provided out-of-the-box. To inject services with business logic into a controller, a third-party dependency injection library implementing the provided interfaces must be added to the project and properly configured.

Although the default routing of requests to action methods in controllers follows the same convention as in ASP.NET Core projects, it is configured elsewhere and with slightly different code which you can find in the RegisterRoutes method of the RouteConfig class:

routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } );

There is also no customizable request pipeline. To hook into a request, several different approaches can be used, depending on what needs to be achieved:

HTTP handlers are used for extension-based request processing. ASP.NET MVC itself is implemented as an HTTP handler which executes action methods in controllers.

HTTP modules are used to hook into the processing of every HTTP request (ASP.NET MVC or not) and are typically used for authentication, logging etc.

Action filters are a part of ASP.NET MVC and can be invoked before or after the action method call, but always after the ASP.NET MVC HTTP handler has already taken over the processing.

From the three approaches above, only action filters are still available in ASP.NET Core. The other two must be converted to middleware. This can make it difficult to port an existing ASP.NET MVC application to ASP.NET Core unless it is very simple. Usually it makes sense to keep maintaining it in ASP.NET MVC, especially if there isn’t a lot of new development being done and the application is not required to run on other operating systems than Windows.

Single-page application (SPA) architecture

In a typical web site or a web application following the MVC architecture as described in the previous section, each user interaction with the web page will result in an HTTP request to the server to which the web server will respond with a new web page for the browser to render.

Figure 4: User interaction in an MVC web application

As the name implies, in a single-page application (SPA) there’s only one HTML page which is downloaded from the web server when the user opens the page. Any further interaction with that page will not directly result in a request to the web server. Instead, it will be handled by the JavaScript code which will update the existing page accordingly.

A new request to the web server will only be made when JavaScript code will require data that is not yet available in the browser.

In response to that request, the web server will not send back a full HTML page. Instead, it will only send the data requested (usually in JSON format). JavaScript code will then process the data received and update the existing page.

Figure 5: User interaction in a single-page application

Single-page application (SPA) pattern is most suitable for web applications with a high level of user interactivity because it can provide a better experience for the user.

Since the final appearance of a page is generated inside the browser and not returned from the server, single-page applications are at a disadvantage when the content must be indexed by search engines. There are solutions for that (i.e. server-side rendering) but they also increase the complexity of the final solution.

Even for a single-page application, the web server part can still take advantage of the MVC pattern. Although the initial web page usually only consists of static files (HTML, CSS and JavaScript) and doesn’t require any server-side processing, the process of generating the JSON documents is not all that different from generating the web pages in an MVC web application. A common term for such backend application is REST service.

ASP.NET Core and ASP.NET Web API

The recommended framework for implementing REST services in .NET is ASP.NET Core (preferably hosted in .NET Core).

In Visual Studio 2019, a new project can be created using the ASP.NET Core Web Application template, but make sure that you choose API in the second step of the project creation wizard. Visual Studio Code users can create a new project from the command line with the following command:

dotnet new webapi

In web API applications, the controller classes are still responsible for responding to incoming requests and can use the business logic implemented in models (and services). However, there’s no need for a view. Instead, action methods can return view models directly, and they will be serialized to JSON format.

[Route("api/[controller]")] [ApiController] public class ValuesController : ControllerBase { // GET api/values [HttpGet] public ActionResult<IEnumerable<string>> Get() { return new string[] { "value1", "value2" }; } }

Although default routing can still be configured in the Configure method of the Startup class, it’s more common to use attribute routing instead because they give more flexibility which is often required for web APIs.

In the example we just saw, the method returns an array of values. A common convention in REST services is to include an ID in the URL to receive only the value corresponding to that ID:

// GET api/values/5 [HttpGet("{id}")] public ActionResult<string> Get(int id) { return "value"; }

When data needs to be modified, other HTTP methods should be used instead of GET , such as:

POST for inserting data,

PUT for modifying data, and

DELETE for deleting data.

Using attributes, such requests can be routed to the appropriate action methods:

// POST api/values [HttpPost] public void Post([FromBody] string value) { } // PUT api/values/5 [HttpPut("{id}")] public void Put(int id, [FromBody] string value) { } // DELETE api/values/5 [HttpDelete("{id}")] public void Delete(int id) { }

The .NET framework predecessor of ASP.NET Core for building REST services is ASP.NET Web API. It’s architecturally similar to ASP.NET MVC.

For the same reasons as ASP.NET MVC, it’s not a recommended choice for new projects, but it makes sense for existing projects to keep using it.

JavaScript frameworks

There’s an abundance of client-side JavaScript frameworks for single-page applications to choose from. Currently, the most popular ones are Angular, React, and Vue.js.

Although there are a lot of differences between them, they consist of the same basic building blocks:

Templates for the HTML pages to be displayed with support for binding values from variables and for handling events such as clicks.

JavaScript (or TypeScript) code that reacts to those events by changing the bound values or switching the HTML template which is currently displayed.

Command line tooling for building the application, for running it locally during development and for other common operations.

There’s no clear choice which framework is the best.

While choosing the one to learn first, you can’t go wrong – just go with any of the ones listed above.

No matter the choice, you will need to have a recent version of Node.js installed on your development machine. I’m going to explain the basic concepts using Angular because this is the framework, I’m most familiar with.

Editorial Note: For Vue.js, check these tutorials.

It’s recommended to have Angular CLI installed globally for any kind of Angular development:

npm install -g @angular/cli

Using it, you can create a new Angular project:

ng new

The application entry point is a static index.html file in the src folder:

<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>AngularApp</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="favicon.ico"> </head> <body> <app-root></app-root> </body> </html>

After the application initializes, the app-root element will be replaced with the root AppComponent .

An Angular application is composed of many components. Each one declares the HTML element name that can be used in a template to insert into.

By convention, its source code is in a file named *.component.ts :

import { Component, Inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; @Component({ selector: 'app-fetch-data', templateUrl: './fetch-data.component.html' }) export class FetchDataComponent { public forecasts: WeatherForecast[]; constructor(http: HttpClient, @Inject('BASE_URL') baseUrl: string) { http.get<WeatherForecast[]>(baseUrl + api/SampleData/WeatherForecasts').subscribe(result => { this.forecasts = result; }, error => console.error(error)); } }

This code will call a REST service to retrieve some data and store it in a local property so that it can be used from its template:

<table class='table table-striped' *ngIf="forecasts"> <thead> <tr> <th>Date</th> <th>Temp. (C)</th> <th>Temp. (F)</th> <th>Summary</th> </tr> </thead> <tbody> <tr *ngFor="let forecast of forecasts"> <td>{{ forecast.dateFormatted }}</td> <td>{{ forecast.temperatureC }}</td> <td>{{ forecast.temperatureF }}</td> <td>{{ forecast.summary }}</td> </tr> </tbody> </table>

In Angular templates, values are interpolated using double curly braces ( {{ }} ). The attributes with *ng prefix are Angular directives which are used to further control how the HTML is rendered.

If we make a rough comparison to ASP.NET Core MVC applications, Angular templates correspond to MVC views. They just use an alternative syntax to Razor in MVC views. The component source code approximately corresponds to MVC controllers. The architecture is somewhat similar to MVC, although it doesn’t match it completely.

ASP.NET Core SPA templates

Typically, a single-page application consists of two separate parts:

the REST service and

the generated static files for the client-side application.

You can decide for yourself how you want to build and host each one. To simplify this, there are ASP.NET Core templates available for Angular and React which join both parts into a single project:

In Visual Studio 2019, you can choose these templates in the second step of the ASP.NET Core Web Application template. They are named: Angular, React.js, and React.js and Redux.

When using the dotnet new command, the template names are: angular , react and reactredux .

Editorial Note: For ASP.NET Core Vue.js Templates, check Daniel’s article www.dotnetcurry.com/aspnet-core/1500/aspnet-core-vuejs-template.

No matter which template you choose, the client-side JavaScript application will be placed in the ClientApp folder. It will be a standard application for the chosen framework which can be fully controlled using its corresponding command line tooling.

In addition to that, the JavaScript application will be fully integrated into the ASP.NET Core application. The generated static files will be hosted as static files in the ASP.NET Core application. During development, the application will also automatically refresh in the browser whenever you change any of its source files.

When starting a new Angular or React application with an ASP.NET Core backend, these templates are probably your best starting point. They make development and deployment more convenient because you don’t have to deal with two separate projects.

Blazor

With increasing support for WebAssembly in modern browsers, JavaScript isn’t the only supported language for applications running in a browser anymore. WebAssembly is a binary format designed to be generated by compilers on one hand, and directly executed in browsers on the other hand. This is opening the doors to frameworks for single-page application development which aren’t JavaScript (or TypeScript) based.

The one most interesting to .NET developers is Blazor which allows you to write your client-side code in C#. Since it’s still in preview, it requires some effort to get it installed. For best experience you will require:

To create a new project, use the ASP.NET Core Web Application template in Visual Studio 2019. In the second step of the wizard, select Blazor (ASP.NET Core hosted). You need to have NET Core 3.0 selected in the dropdowns at the top to make it available.

To create a project from the command line instead, you first need to install the Blazor templates:

dotnet new -i Microsoft.AspNetCore.Blazor.Templates::3.0.0-preview4-19216-03 dotnet new blazorhosted

The generated solution is functionally similar to the projects generated by the ASP.NET Core SPA templates. It consists of three projects:

*.Server contains the ASP.NET Core application with the web API.

contains the ASP.NET Core application with the web API. *.Client contains the Blazor single-page application.

contains the Blazor single-page application. *.Shared contains classes which can be shared between the two projects because they both use the same language.

The ASP.NET Core application is almost identical to the one for the JavaScript SPA frameworks. The Blazor application is functionally very similar to the Angular and React applications as well. It’s just developed using a different technology which makes the code more similar to an ASP.NET Core MVC application than a JavaScript SPA.

The source code for both pages and components is placed in *.razor files, and it uses Razor syntax:

@page "/fetchdata" @using AspNetCoreBlazor.Shared @inject HttpClient Http <h1>Weather forecast</h1> <p>This component demonstrates fetching data from the server.</p> @if (forecasts == null) { <p><em>Loading...</em></p> } else { <table class="table"> <thead> <tr> <th>Date</th> <th>Temp. (C)</th> <th>Temp. (F)</th> <th>Summary</th> </tr> </thead> <tbody> @foreach (var forecast in forecasts) { <tr> <td>@forecast.Date.ToShortDateString()</td> <td>@forecast.TemperatureC</td> <td>@forecast.TemperatureF</td> <td>@forecast.Summary</td> </tr> } </tbody> </table> }

This markup could easily be mistaken for a view in an ASP.NET Core MVC application. The biggest difference is that there’s no controller class. The rest of code is contained in a @functions block, usually placed at the bottom of the same file:

@functions { WeatherForecast[] forecasts; protected override async Task OnInitAsync() { forecasts = await Http.GetJsonAsync<WeatherForecast[]>("api/SampleData/WeatherForecasts"); } }

Although this code is running in the browser, it uses classes from .NET Standard 2.0 instead of browser or JavaScript APIs.

To a .NET developer with little or no JavaScript experience, Blazor can look very tempting. However, it’s not suitable for production use (yet) for several reasons:

Most importantly, the framework is still in preview and no official release has been announced yet. It will almost certainly be released after .NET Core 3.0.

The current implementation of Blazor is very inefficient. The code is not compiled directly to WebAssembly. Instead, it is compiled into a .NET assembly which is interpreted in the browser with a .NET runtime which was compiled to WebAssembly. This might still change until the final release.

WebAssembly currently can’t access DOM (Document Object Model) APIs directly. It can only call JavaScript functions which then interact with the DOM. Since DOM APIs are used for any client-side modification of the HTML page, this negatively affects WebAssembly performance in this field.

At least until the first official release of Blazor, JavaScript frameworks are a superior choice for development of single-page applications. Blazor is currently only an interesting experiment showing what might be possible in the future. To learn more about it, you can read a dedicated article about it in the DNC Magazine written by Daniel Jimenez Garcia: Blazor - .NET in the browser.

Client-side web applications running on the server (MVC vs SPA)

There are two main differences between web applications following the MVC pattern and those following the SPA pattern:

MVC applications run on the server, while SPAs run on the client. With the former, the browser acts as a thin client sending user requests to the server and rendering the received responses for the user. With the latter, the browser is a fat client which only needs the web server to get the application files at the beginning and to retrieve additional data while the application is running.

The programming model of MVC applications fully exposes the nature of the web: every interaction is an HTTP request and every response is the new state of the web page. The SPA programming model is much more similar to desktop applications. User interactions trigger event handlers which modify the existing page currently displayed in the browser. HTTP requests are only used for data access operations (and loading of static files).

There’s a third category of applications which are a hybrid between the two approaches. They are running on the server, but their programming model is event-driven, “hiding” the request/response nature of the web from the developer.

.NET developers can choose between two frameworks for development of such applications.

Razor components

Razor components were originally named Blazor server-side. That name is a pretty good description of what they are about.

A Razor components application is an ASP.NET Core application configured to run Blazor code on the server. This means that the .NET Standard assemblies built from the .razor files are not interpreted in the browser. Instead, the browser is only running a small SignalR-based JavaScript library which uses WebSockets for real-time communication with the server where those assemblies are running.

The requirements for using Razor components are currently the same as for Blazor: the latest previews of .NET Core 3.0 SDK and Visual Studio 2019 are recommended. To create a new project in Visual Studio 2019, the ASP.NET Core Web Application template must be used, and the Razor Components option must be selected in the second step of the wizard.

From command line the same can be achieved using the following command:

dotnet new blazorserverside

In the generated solution, there’s only a single project because all the code is running on the server. The setup code specific to running Razor components on the server is placed in the Startup class. The code in the .razor files in the Pages folder is almost identical to the code from the Blazor sample project. The only important difference can be found in the @functions block of the FetchData.razor file:

@functions { WeatherForecast[] forecasts; protected override async Task OnInitAsync() { forecasts = await ForecastService.GetForecastAsync(DateTime.Now); } }

Here, the sample data is retrieved by calling an in-process service.

If you look at the Blazor sample, you can see that an HTTP request was used there instead. Since data resides on the server and Blazor is running on the client, the HTTP request was the only option there. The HTTP request approach would work with Razor components just as well, but it’s probably not used in the sample because it would introduce additional overhead of implementing the web API and serializing the response to JSON format.

Razor components don’t have any of the previously listed disadvantages of Blazor:

Although they are still in preview, they will be officially released as part of .NET Core 3.0.

.NET assemblies are running more efficiently since they don’t need to be interpreted in the browser.

The HTML is being modified by the JavaScript library in the browser which can directly use the DOM API.

However, since the code is running on the server, the browser must be constantly connected to it. As soon as the connection is broken, the application stops working. There’s no way to support offline mode with this approach, unlike SPAs which can continue working without interruption even with no internet connection, at least until they require new data from the server.

When Razor components are officially released with .NET Core 3.0, they will become a viable alternative to JavaScript frameworks for development of highly interactive web applications, at least for scenarios where reliable connectivity to the server is not an issue. If you strictly use REST service calls for retrieving data which would reside on the server if the application was a SPA, you might even be able to convert your application to a Blazor SPA without too much effort when Blazor is officially released.

ASP.NET Web Forms

ASP.NET Web Forms take a different approach to providing the illusion of a client-side programming model to the developer. Similar to MVC applications, all the communication between the client and the server is using the stateless approach of HTTP requests and responses. The state necessary for the application to function correctly is being sent across as part of these requests and responses in the background without the developer having to be aware of it.

The client-side development model is further emphasized by server controls which can be used in the markup in addition to standard HTML elements. The server processes these controls before sending the page to the client and generates corresponding HTML markup to ensure correct rendering in the browser. The following snippet from the Wingtip Toys sample application would generate a table with typical grid view semantics:

<asp:GridView ID="CartList" runat="server" AutoGenerateColumns="False" ShowFooter="True" GridLines="Vertical" CellPadding="4" ItemType="WingtipToys.Models.CartItem" SelectMethod="GetShoppingCartItems" CssClass="table table-striped table-bordered" > <Columns> <asp:BoundField DataField="ProductID" HeaderText="ID" SortExpression="ProductID" /> <asp:BoundField DataField="Product.ProductName" HeaderText="Name" /> <asp:BoundField DataField="Product.UnitPrice" HeaderText="Price (each)" DataFormatString="{0:c}"/> <asp:TemplateField HeaderText="Quantity"> <ItemTemplate> <asp:TextBox ID="PurchaseQuantity" Width="40" runat="server" Text="<%#: Item.Quantity %>"></asp:TextBox> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Item Total"> <ItemTemplate> <%#: String.Format("{0:c}", ((Convert.ToDouble(Item.Quantity)) * Convert.ToDouble(Item.Product.UnitPrice)))%> </ItemTemplate> </asp:TemplateField> <asp:TemplateField HeaderText="Remove Item"> <ItemTemplate> <asp:CheckBox id="Remove" runat="server"></asp:CheckBox> </ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView>

Instead of Razor syntax, the markup above is using the ASPX view engine. Razor was developed as an alternative to it in the early versions of ASP.NET MVC.

Apart from data binding, the markup above also specifies that the GetShoppingCartItems method will be invoked when the data for the table needs to be retrieved. This method is implemented in the accompanying code file (commonly called code-behind file):

public List<CartItem> GetShoppingCartItems() { ShoppingCartActions actions = new ShoppingCartActions(); return actions.GetCartItems(); }

There’s nothing special about how this code is invoked because the data is usually retrieved before the generated web page is sent to the browser. However, the same approach is also used to define a click event handler for a button:

<asp:Button ID="UpdateBtn" runat="server" Text="Update" OnClick="UpdateBtn_Click" />

This event is triggered when the user clicks on the button in the browser.

To trigger the event on the server, the button click will generate a POST request to the server with the page state and the information about the event by submitting the form that is automatically added to any Web Forms page.

In response, the server will execute the code in the event handler (along with any pre- and post-processing required) and send the updated web page in response.

As you can see, at the network level, the communication between the client and the server is similar to MVC applications. On top of it, ASP.NET Web Forms provide a client-side programming model abstraction but at the same time takes away full control over the generated HTML from the developer.

ASP.NET Web Forms are only available for the .NET framework. They weren’t reimplemented for ASP.NET Core like the other ASP.NET web development frameworks.

To create a new ASP.NET Web Forms project in Visual Studio 2019, start with the ASP.NET Web Application (.NET Framework) template and select Web Forms in the second step of the wizard. However, I wouldn’t recommend using ASP.NET Web Forms for new projects anymore for several reasons:

ASP.NET Web Forms have no successor available for .NET Core. This means that your application will only run on Windows and there’s no path available for migrating the code to .NET Core in the future.

There’s no active development on ASP.NET Web Forms anymore. There were only a few minor new features added in recent versions of the .NET frameworks, and you can expect even less of that in the future.

You have limited control over the generated HTML when using ASP.NET Web Forms. This can be an issue when having to exactly implement a specific design or make the page responsive for mobile devices and different screen sizes.

For existing ASP.NET Web Forms applications, you don’t have a lot of choice. You will have to keep maintaining them in their current form unless you decide for a complete rewrite in one of the other development frameworks described in previous sections.

Conclusion:

There are many approaches available for developing a web application in .NET. However, in most cases, you will choose between two of them based on the type of the application you’re developing.

For content-focused public sites which depend on good indexing by search engines, the best fit is usually an MVC web application in ASP.NET Core.

For web applications with a lot of user interaction, potentially protected by a login, a single-page application (SPA) in your preferred JavaScript framework with an ASP.NET Core web API backend is often a better choice.

For real-world applications which usually fall somewhere in between the two extremes, the advantages and disadvantages of each approach should be carefully considered when making the choice for one or the other.

With the release of .NET Core, Razor components will become a good alternative to JavaScript SPAs if you prefer C# to JavaScript (or TypeScript) and the requirement for a constant connection to the web server is not a serious limitation for your application.

The web frameworks in the .NET framework are not really recommended for starting the development of new applications. Instead, you should use .NET Core. For existing applications developed using the .NET framework, you can keep maintaining them using the same technology, unless you require features the framework does not support, e.g. you want to run your application on Windows, as well as other operating systems too.

This article has been editorially reviewed by Suprotim Agarwal.

C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn. We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle). Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview. Click here to Explore the Table of Contents or Download Sample Chapters!