Aspnet Models
title: ASP.NET Core Models — Complete Guide to Data and Validation description: 'Learn ASP.NET Core models: data annotations for validation, model binding, ViewModels, DTOs, AutoMapper, and custom validation attributes for clean data handling.' date: 2026-06-28 lastmod: 2026-06-28 weight: 17 tags: [backend, aspnet]
ASP.NET Core models represent application data with validation rules via data annotations, supporting model binding from HTTP requests and automatic validation in controllers.
## What You'll Learn
By the end of this tutorial, you'll create models with data annotations, implement custom validation, use ViewModels and DTOs, configure AutoMapper, and handle model binding errors.
## Real-World Use
A registration form model uses [Required], [EmailAddress], and [StringLength] attributes. Invalid submissions return validation errors to the UI with field-level messages.
## Models Learning Path
```mermaid
flowchart LR
A[Views] --> B[Models]
B --> C[Routing]
C --> D[Middleware]
D --> E[DI]
B --> F{You Are Here}
style F fill:#f90,color:#fff
Model with Data Annotations
using System.ComponentModel.DataAnnotations;
public class RegisterViewModel
{
[Required(ErrorMessage = "Username is required")]
[StringLength(50, MinimumLength = 3)]
public string Username { get; set; } = string.Empty;
[Required]
[EmailAddress(ErrorMessage = "Invalid email format")]
public string Email { get; set; } = string.Empty;
[Required]
[StringLength(100, MinimumLength = 8)]
[DataType(DataType.Password)]
public string Password { get; set; } = string.Empty;
[Compare("Password", ErrorMessage = "Passwords do not match")]
[DataType(DataType.Password)]
public string ConfirmPassword { get; set; } = string.Empty;
}
Custom Validation
public class MinimumAgeAttribute : ValidationAttribute
{
private readonly int _minimumAge;
public MinimumAgeAttribute(int minimumAge)
{
_minimumAge = minimumAge;
}
protected override ValidationResult? IsValid(object? value, ValidationContext context)
{
if (value is DateTime dateOfBirth)
{
var age = DateTime.Today.Year - dateOfBirth.Year;
if (dateOfBirth > DateTime.Today.AddYears(-age)) age--;
if (age < _minimumAge)
return new ValidationResult($"Must be at least {_minimumAge} years old");
}
return ValidationResult.Success;
}
}
public class UserProfile
{
[MinimumAge(18, ErrorMessage = "You must be 18+")]
public DateTime DateOfBirth { get; set; }
}
ViewModel vs DTO
// Domain Model (Entity Framework)
public class User
{
public int Id { get; set; }
public string Username { get; set; } = "";
public string Email { get; set; } = "";
public string PasswordHash { get; set; } = "";
public DateTime CreatedAt { get; set; }
}
// ViewModel (for views)
public class UserProfileViewModel
{
public string Username { get; set; } = "";
public string Email { get; set; } = "";
public string? Bio { get; set; }
}
// DTO (for API responses)
public record UserDto(
int Id,
string Username,
string Email,
DateTime CreatedAt
);
AutoMapper Configuration
dotnet add package AutoMapper.Extensions.Microsoft.DependencyInjection
// Mapping profile
public class UserProfile : Profile
{
public UserProfile()
{
CreateMap<User, UserDto>();
CreateMap<User, UserProfileViewModel>();
CreateMap<RegisterViewModel, User>()
.ForMember(dest => dest.PasswordHash,
opt => opt.MapFrom(src => BCrypt.HashPassword(src.Password)));
}
}
// Usage in controller
[ApiController]
public class UsersController : ControllerBase
{
private readonly IMapper _mapper;
public UsersController(IMapper mapper) => _mapper = mapper;
[HttpGet("{id}")]
public ActionResult<UserDto> Get(int id)
{
var user = _userService.GetById(id);
return Ok(_mapper.Map<UserDto>(user));
}
}
Model Binding in Controller
[HttpPost("register")]
public IActionResult Register(RegisterViewModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState); // Returns all validation errors
}
var user = _mapper.Map<User>(model);
_userService.Create(user);
return Ok();
}
Common Mistakes
1. Exposing Domain Models Directly
Domain models contain internal data (PasswordHash). Map to ViewModels or DTOs before returning.
2. Not Checking ModelState.IsValid
Without checking, invalid models pass through. Always check ModelState.IsValid in every POST action.
3. Mixing Validation and DB Concerns
Models with both [Required] and [ForeignKey] do too much. Separate validation models from persistence models.
4. Overusing ViewBag for Data
ViewBag is untyped. Create ViewModels with specific properties for strong typing and IntelliSense.
5. Not Using [Bind] for Security
Without [Bind(Include = "Property1,Property2")], over-posting attacks can set properties you didn't intend.
Practice Questions
1. What are data annotations?
Attributes like [Required], [StringLength], [Range] that define validation rules on model properties.
2. How do you create a custom validation attribute?
Inherit from ValidationAttribute and override IsValid() method with your validation logic.
3. What is the difference between ViewModel and DTO?
ViewModel is shaped for a specific view (includes display data). DTO is for API transfers (typically matches the data contract).
4. How does AutoMapper simplify development?
AutoMapper automatically maps properties between objects (User -> UserDto), reducing boilerplate mapping code.
5. Challenge: Create a registration model with 5 validation rules and a custom password strength validator.
public class StrongPasswordAttribute : ValidationAttribute
{
protected override ValidationResult? IsValid(object? value, ValidationContext ctx)
{
var password = value as string;
if (password == null || !password.Any(char.IsUpper)
|| !password.Any(char.IsLower)
|| !password.Any(char.IsDigit))
return new ValidationResult("Password needs upper, lower, and digit");
return ValidationResult.Success;
}
}
FAQ
Mini Project: Registration Model
Create a complete user registration model with validation.
public class RegisterViewModel : IValidatableObject
{
[Required, StringLength(50, MinimumLength = 3)]
public string Username { get; set; } = "";
[Required, EmailAddress]
public string Email { get; set; } = "";
[Required, DataType(DataType.Password)]
public string Password { get; set; } = "";
[Compare("Password")]
public string ConfirmPassword { get; set; } = "";
public IEnumerable<ValidationResult> Validate(ValidationContext ctx)
{
if (Password == Username)
yield return new ValidationResult("Password cannot match username");
}
}
What's Next
ASP.NET Core Routing ASP.NET Core Middleware
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro