ASP.NET MVC is a web application framework developed by Microsoft that implements the model-view-controller (MVC) pattern. It provides an alternative to ASP.NET Web Forms for building web applications, offering more control over HTML, better separation of concerns, and testability.
Represents the application data and business logic. Models contain the core application logic and data.
Handles the UI display (typically HTML templates). Views are responsible for presenting data to the user.
Processes incoming requests, works with models, and selects views. Controllers handle user input and interaction.
| Feature | ASP.NET MVC | Web Forms |
|---|---|---|
| HTML Control | Full control over rendered HTML | Limited control (server controls) |
| Performance | Better (no view state overhead) | View state adds overhead |
| Testability | Highly testable | Harder to test |
| URLs | Clean, SEO-friendly | File extensions in URLs |
| Modern Web | Better support for REST, AJAX | Postback model |
1. Routing → 2. Controller Initialization → 3. Action Execution → 4. View Rendering → 5. Response
MVC is a software design pattern that separates an application into three main logical components: Model, View, and Controller. This separation helps manage complexity in large applications.
Represents the data and business logic of the application
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public bool IsExpensive()
{
return Price > 1000;
}
}
Presents the data to the user and handles user interface
@model Product
<h2>@Model.Name</h2>
<p>Price: $@Model.Price</p>
@if(Model.IsExpensive())
{
<span class="warning">Premium Product</span>
}
Handles user requests and coordinates between Model and View
public class ProductController : Controller
{
public ActionResult Details(int id)
{
var product = _repository.GetProduct(id);
if(product == null)
return HttpNotFound();
return View(product);
}
}
| Component | Interacts With | Direction | Purpose |
|---|---|---|---|
| Controller ↔ View | Controller passes model to View | One-way (typically) | Display data |
| Controller ↔ Model | Controller updates/reads Model | Two-way | Business logic execution |
| View → Controller | View sends user actions | One-way | User input handling |
1. User Request → 2. Routing → 3. Controller Initialization → 4. Controller Action → 5. Model Interaction → 6. View Selection → 7. View Rendering → 8. Response
/Controllers
ProductController.cs
HomeController.cs
/Models
Product.cs
Order.cs
/Views
/Product
Index.cshtml
Details.cshtml
/Shared
_Layout.cshtml
| Feature | ASP.NET MVC | Web Forms |
|---|---|---|
| Architecture | Model-View-Controller (MVC) pattern | Event-driven model (similar to WinForms) |
| Control Over HTML | Full control over rendered HTML | Limited control (uses server controls) |
| State Management | Stateless (no view state) | Uses ViewState and PostBack |
| URL Structure | Clean, RESTful URLs (Routing) | File-based URLs (.aspx extensions) |
| Testability | Highly testable (unit testing friendly) | Difficult to unit test |
| Performance | Faster (lighter weight, no view state) | Slower (view state overhead) |
| Development Style | Separation of concerns | Rapid Application Development (RAD) |
| Learning Curve | Steeper (requires understanding of patterns) | Easier for beginners |
| Page Lifecycle | Simple request/response | Complex page lifecycle events |
| Modern Web Support | Better for AJAX, REST APIs, SPAs | Designed for traditional web apps |
| Community & Future | Active development (part of ASP.NET Core) | Maintenance mode (not in .NET Core) |
// No automatic state persistence
// Uses HTTP verbs explicitly:
[HttpPost]
public ActionResult Create(Product product)
{
// Handle form submission
}
// Automatic state management
protected void Button_Click(object sender, EventArgs e)
{
// Control values persist automatically
Label1.Text = TextBox1.Text;
}
// Controller
[HttpPost]
public ActionResult Register(User user)
{
if(ModelState.IsValid)
{
_service.Register(user);
return RedirectToAction("Success");
}
return View(user);
}
// View (Razor)
@model User
@using (Html.BeginForm())
{
@Html.TextBoxFor(m => m.Username)
@Html.ValidationMessageFor(m => m.Username)
<input type="submit" value="Register" />
}
// Code-behind
protected void btnRegister_Click(object sender, EventArgs e)
{
if(Page.IsValid)
{
var user = new User {
Username = txtUsername.Text
};
_service.Register(user);
Response.Redirect("Success.aspx");
}
}
// ASPX Page
<asp:TextBox ID="txtUsername" runat="server" />
<asp:RequiredFieldValidator
ControlToValidate="txtUsername"
runat="server" ErrorMessage="Required" />
<asp:Button ID="btnRegister"
runat="server" Text="Register"
OnClick="btnRegister_Click" />
| Metric | ASP.NET MVC | Web Forms |
|---|---|---|
| Memory Usage | Lower (no view state) | Higher (view state storage) |
| Request Processing | Faster (leaner pipeline) | Slower (complex lifecycle) |
| Page Size | Smaller (clean HTML) | Larger (view state, control IDs) |
| Scalability | Better (stateless) | Good (but stateful) |
The MVC framework enforces a convention-based folder structure that promotes organization and separation of concerns.
MVC_Application/ ├── App_Data/ # Database files, XML storage, etc. ├── App_Start/ # Configuration classes │ ├── BundleConfig.cs # Script/CSS bundling │ ├── FilterConfig.cs # Global filters │ ├── RouteConfig.cs # URL routing rules │ └── WebApiConfig.cs # Web API configuration │ ├── Controllers/ # Controller classes │ ├── HomeController.cs # Default controller │ ├── AccountController.cs # Authentication controller │ └── ProductController.cs # Example business controller │ ├── Models/ # Business logic and data models │ ├── Product.cs # Domain model │ ├── AccountViewModels.cs # View-specific models │ └── Repository.cs # Data access layer │ ├── Views/ # Razor view files (.cshtml) │ ├── Home/ # Views for HomeController │ │ ├── Index.cshtml # Default view │ │ └── About.cshtml # Secondary view │ │ │ ├── Account/ # Views for AccountController │ │ ├── Login.cshtml │ │ └── Register.cshtml │ │ │ ├── Product/ # Views for ProductController │ │ ├── Index.cshtml # List view │ │ ├── Details.cshtml # Detail view │ │ └── Edit.cshtml # Edit form │ │ │ ├── Shared/ # Reusable components │ │ ├── _Layout.cshtml # Master page │ │ ├── _Navigation.cshtml # Partial view │ │ └── Error.cshtml # Error page │ │ │ └── Web.config # View-specific config │ ├── Scripts/ # JavaScript files │ ├── jquery-{version}.js │ ├── modernizr-{version}.js │ └── site.js # Application scripts │ ├── Content/ # CSS and static assets │ ├── Site.css # Main stylesheet │ └── Images/ # Application images │ ├── Areas/ # Logical sections (optional) │ ├── Admin/ # Admin section │ │ ├── Controllers/ │ │ ├── Models/ │ │ └── Views/ │ └── API/ # Web API section │ ├── Global.asax # Application lifecycle events └── Web.config # Application configuration
| Folder | Purpose | Important Files |
|---|---|---|
| App_Start | Configuration classes executed at application startup | RouteConfig.cs, BundleConfig.cs |
| Controllers | Contains controller classes that handle requests | [Name]Controller.cs files |
| Views | Contains Razor views organized by controller | .cshtml files, _Layout.cshtml |
| Models | Contains business logic and data models | Domain models, ViewModels |
| Areas | Separate functional sections of the application | Admin, API sections |
Views are organized in subfolders that must match controller names:
/Views
/Home ← For HomeController
Index.cshtml
/Product ← For ProductController
Index.cshtml
Details.cshtml
The framework automatically looks for views in these corresponding folders.
Master page template containing common HTML structure
<!DOCTYPE html>
<html>
<head>
@RenderSection("styles", required: false)
</head>
<body>
@RenderBody()
@RenderSection("scripts", required: false)
</body>
</html>
Executes before any view to set default layout
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
Contains common directives for all views
@using MyApplication.Models @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
| Component | Naming Pattern | Example |
|---|---|---|
| Controllers | [Name]Controller.cs | ProductController.cs |
| Views | ActionName.cshtml | Index.cshtml, Details.cshtml |
| Layouts | _Layout.cshtml | _Layout.cshtml, _AdminLayout.cshtml |
| Partial Views | _PartialName.cshtml | _ProductTable.cshtml |
| View Models | [Purpose]ViewModel.cs | ProductEditViewModel.cs |
The Model represents the application's data and business logic in the MVC (Model-View-Controller) architecture. It encapsulates the application state and rules for manipulating that state.
Defines the structure of your application data (entities, properties, relationships)
Contains validation rules, calculations, and business processes
Handles communication with databases or other data sources
Maintains and manages the application's current state
| Type | Purpose | Example |
|---|---|---|
| Domain Model | Core business entities | Product, Customer, Order |
| View Model | Data shaped for specific views | ProductEditViewModel |
| Data Transfer Object (DTO) | Data structure for API communication | ProductApiResponse |
public class Product
{
public int Id { get; set; }
[Required(ErrorMessage = "Name is required")]
[StringLength(100)]
public string Name { get; set; }
[Range(0.01, 10000)]
public decimal Price { get; set; }
public bool IsInStock { get; set; }
// Business logic method
public bool IsExpensive()
{
return Price > 1000;
}
}
public class ProductCreateViewModel
{
[Display(Name = "Product Name")]
public string Name { get; set; }
[DataType(DataType.Currency)]
public decimal Price { get; set; }
// Additional view-specific properties
public List<SelectListItem> Categories { get; set; }
public int SelectedCategoryId { get; set; }
}
public class UserRegistrationModel
{
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[StringLength(100, MinimumLength = 6)]
[DataType(DataType.Password)]
public string Password { get; set; }
[Compare("Password", ErrorMessage = "Passwords don't match")]
public string ConfirmPassword { get; set; }
}
[Required] - Field is mandatory[StringLength] - Limits string length[Range] - Value must be between specified numbers[EmailAddress] - Validates email format[RegularExpression] - Custom pattern matchingEach model should represent a single concept or entity
Use different models for domain logic vs. view representation
Place validation rules in models (not controllers or views)
Business logic belongs in models, not controllers
Create specific models for views when needed
Request → Controller → Model (Business Logic/Data Access) → Controller → View
↑_________________________________________________________↓
Data Annotations are attributes used to add metadata and validation rules to model properties. They provide a declarative way to define validation requirements that are enforced both client-side and server-side.
| Attribute | Purpose | Example |
|---|---|---|
[Required] |
Ensures the property has a value | [Required(ErrorMessage="Name is required")] |
[StringLength] |
Specifies minimum and maximum string length | [StringLength(50, MinimumLength=3)] |
[Range] |
Constrains value to a specified range | [Range(18, 120)] |
[RegularExpression] |
Validates against a regex pattern | [RegularExpression(@"^\d{3}-\d{3}-\d{4}$")] |
[EmailAddress] |
Validates email format | [EmailAddress] |
[Compare] |
Compares two property values | [Compare("Password")] |
[DataType] |
Specifies data type for UI hints | [DataType(DataType.Password)] |
[Display] |
Specifies display name | [Display(Name="Full Name")] |
public class UserRegistrationModel
{
[Required(ErrorMessage = "Email is required")]
[EmailAddress(ErrorMessage = "Invalid email format")]
[Display(Name = "Email Address")]
public string Email { get; set; }
[Required]
[StringLength(100, MinimumLength = 8,
ErrorMessage = "Password must be 8-100 characters")]
[DataType(DataType.Password)]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm Password")]
[Compare("Password",
ErrorMessage = "Passwords do not match")]
public string ConfirmPassword { get; set; }
[Range(typeof(bool), "true", "true",
ErrorMessage = "Must accept terms")]
[Display(Name = "Accept Terms")]
public bool AcceptTerms { get; set; }
}
[HttpPost]
public ActionResult Register(UserRegistrationModel model)
{
if (ModelState.IsValid)
{
// Process valid data
return RedirectToAction("Success");
}
// Return to form with validation errors
return View(model);
}
@model UserRegistrationModel
@using (Html.BeginForm())
{
<div class="form-group">
@Html.LabelFor(m => m.Email)
@Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.Email)
</div>
<div class="form-group">
@Html.LabelFor(m => m.Password)
@Html.PasswordFor(m => m.Password, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.Password)
</div>
<!-- Other fields -->
<input type="submit" value="Register" class="btn btn-primary" />
}
public class FutureDateAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
if (value is DateTime date)
{
return date > DateTime.Now;
}
return false;
}
}
// Usage:
[FutureDate(ErrorMessage = "Date must be in the future")]
public DateTime EventDate { get; set; }
public class EventModel : IValidatableObject
{
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public IEnumerable<ValidationResult> Validate(
ValidationContext validationContext)
{
if (EndDate < StartDate)
{
yield return new ValidationResult(
"End date must be after start date",
new[] { nameof(EndDate) });
}
}
}
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script> <script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
appsettings.json has:
"ClientValidationEnabled": true, "UnobtrusiveJavaScriptEnabled": true
1. User submits form → 2. Client-side validation (if enabled) → 3. Server receives request → 4. Model binding with validation → 5. Controller checks ModelState.IsValid → 6. Either processes data or returns errors → 7. View displays validation messages
Automatic mapping of HTTP request data to action parameters and model properties
Model Binding is a fundamental feature of ASP.NET MVC that automatically:
| Source | Description | When Used |
|---|---|---|
| Form Values | POSTed form fields (application/x-www-form-urlencoded or multipart/form-data) | Form submissions, file uploads |
| Route Values | URL path segments (/{controller}/{action}/{id}) | Clean URLs, RESTful routes |
| Query Strings | URL parameters (?name=value&page=1) | GET requests, filtering data |
| JSON Body | Request body (application/json) | API requests, AJAX calls |
Control binding behavior using these attributes:
// Bind from specific sources
public ActionResult GetUser(
[FromQuery] string searchTerm, // From URL query string
[FromRoute] int id, // From route parameters
[FromForm] User model, // From form fields
[FromBody] Order order, // From request body (JSON/XML)
[FromHeader(Name="Accept")] string acceptHeader // From headers
) { ... }
Model Binding automatically handles:
public class OrderViewModel {
public Customer Customer { get; set; }
public List<OrderItem> Items { get; set; }
public Dictionary<string, string> Metadata { get; set; }
}
// Automatically binds complex object from form/JSON
[HttpPost]
public ActionResult PlaceOrder(OrderViewModel order) { ... }
Works automatically with DataAnnotations:
public class RegisterModel {
[Required(ErrorMessage = "Email is required")]
[EmailAddress]
public string Email { get; set; }
[Required]
[StringLength(100, MinimumLength = 6)]
[DataType(DataType.Password)]
public string Password { get; set; }
[Compare("Password", ErrorMessage = "Passwords don't match")]
public string ConfirmPassword { get; set; }
}
[HttpPost]
public ActionResult Register(RegisterModel model) {
if (ModelState.IsValid) {
// Process valid data
}
return View(model);
}
Views that are bound to specific model types for compile-time safety and IntelliSense support
Strongly typed views are Razor views that:
@model directive
@model MyApplication.Models.Product
<h2>@Model.Name</h2>
<p>Price: @Model.Price.ToString("C")</p>
<p>In Stock: @Model.InStock</p>
@* IntelliSense works on Model.* properties *@
Helpers that work with model properties:
@model MyApplication.Models.User
@using (Html.BeginForm())
{
<div class="form-group">
@Html.LabelFor(m => m.FirstName)
@Html.TextBoxFor(m => m.FirstName, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.FirstName)
</div>
<div class="form-group">
@Html.LabelFor(m => m.Email)
@Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.Email)
</div>
}
// ViewModel definition
public class ProductDetailViewModel
{
public Product Product { get; set; }
public List<Review> Reviews { get; set; }
public bool ShowAdminControls { get; set; }
}
// Controller action
public ActionResult Detail(int id)
{
var viewModel = new ProductDetailViewModel
{
Product = _repository.GetProduct(id),
Reviews = _repository.GetReviews(id),
ShowAdminControls = User.IsInRole("Admin")
};
return View(viewModel);
}
// View
@model MyApplication.ViewModels.ProductDetailViewModel
<h2>@Model.Product.Name</h2>
@if(Model.ShowAdminControls)
{
@Html.ActionLink("Edit", "Edit", new { id = Model.Product.Id })
}
<h3>Reviews (@Model.Reviews.Count)</h3>
@foreach(var review in Model.Reviews)
{
<div>@review.Text</div>
}
@model List<MyApplication.Models.Product>
<table class="table">
<tr>
<th>Name</th>
<th>Price</th>
</tr>
@foreach(var product in Model)
{
<tr>
<td>@product.Name</td>
<td>@product.Price.ToString("C")</td>
</tr>
}
</table>
@model at the top of your view@Html.EditorFor and @Html.DisplayFor templates
// Partial view definition (_ProductCard.cshtml)
@model MyApplication.Models.Product
<div class="product-card">
<h3>@Model.Name</h3>
<p>@Model.Price.ToString("C")</p>
@Html.ActionLink("Details", "Details", new { id = Model.Id })
</div>
// Using the partial in main view
@foreach(var product in Model.Products)
{
@Html.Partial("_ProductCard", product)
}
// In Views/Shared/EditorTemplates/Product.cshtml
@model MyApplication.Models.Product
<div class="form-group">
@Html.LabelFor(m => m.Name)
@Html.TextBoxFor(m => m.Name, new { @class = "form-control" })
</div>
// In main view
@model MyApplication.Models.Product
@using (Html.BeginForm())
{
@Html.EditorForModel() // Uses the editor template
<input type="submit" value="Save" />
}
| Aspect | Domain Models | ViewModels |
|---|---|---|
| Purpose | Represent business/data layer entities | Shape data specifically for views |
| Scope | Application-wide business logic | View-specific presentation logic |
| Structure | Mirrors database tables/entities | Optimized for UI requirements |
| Validation | Business rule validation | UI input validation |
| Usage | Used throughout application layers | Used only between controller and view |
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public int CategoryId { get; set; }
public Category Category { get; set; }
// Business logic
public bool IsPremium() => Price > 1000;
}
public class ProductViewModel
{
public int Id { get; set; }
[Required]
[Display(Name = "Product Name")]
public string Name { get; set; }
[Range(0.01, 10000)]
public decimal Price { get; set; }
// View-specific properties
public List<SelectListItem> Categories { get; set; }
public int SelectedCategoryId { get; set; }
// UI helper properties
public bool ShowDiscount { get; set; }
public string FormattedPrice => Price.ToString("C");
}