Data Annotation Attributes in ASP.NET Core MVC

Data Annotation Attributes in ASP.NET Core MVC

In this article, I will discuss how to Perform Model Validations using Data Annotation Attributes in an ASP.NET Core MVC Application with one Real-time Example. Please read our previous article on Model Validations in ASP.NET Core MVC. At the end of this article, you will understand how to use all Built-in data annotation attributes in ASP.NET Core MVC Application along with Entity Framework Core.

Built-in Data Annotation Attributes in ASP.NET Core MVC

ASP.NET Core MVC provides a rich set of Data Annotation Attributes under the System.ComponentModel.DataAnnotations namespace to facilitate model validation. These attributes ensure data integrity by enforcing validation rules on model properties both on the client and server sides. The following are the Commonly Used Data Annotation Attributes in ASP.NET Core MVC Applications:

  • [Required]: Marks a property as mandatory.
  • [MinLength(length)] & [MaxLength(length)]: Define the minimum and maximum length for string properties.
  • [StringLength(maxLength, MinimumLength = minLength)]: Specifies minimum and maximum lengths.
  • [Range(min, max)]: Restricts numerical values within a specified range.
  • [EmailAddress]: Validates the format of an email address.
  • [RegularExpression(pattern)]: Ensures the property matches a specific regex pattern.
  • [Compare(“OtherProperty”)]: Compares two properties for equality (e.g., password confirmation).
  • [DataType(type)]: Specifies the type of data (e.g., Date, URL, Phone).
Real-time Example to Understand Data Annotation Attributes in ASP.NET Core MVC

Let us understand the above Attributes by taking one real-time example. We will create the following Employee Creation Form with Proper Validation on the Client and Server Sides using the Built-in Data Annotation Attributes.

Basic Employee Information:

We will ask the user to enter the basic details of the Employee, such as Name, Email, Gender, and Date of Birth, as shown in the below image. With invalid data, we will get the following error message:

Real-time Example to Understand Data Annotation Attributes in ASP.NET Core MVC

Employee Address Information:

We will then ask the user to enter the Address details of the Employee, such as Street, City, State, and Postal Code, as shown in the image below. With invalid data, we will get the following error message:

Data Annotation Attributes in ASP.NET Core MVC

Employee Job Details:

We will then ask the user to enter the Job details of the Employee, such as Job Title, Department, Salary, and Skills, as shown in the below image. With invalid data, we will get the following error message:

Model Validation using Data Annotation Attributes in ASP.NET Core MVC

Employee Account Information:

As shown in the image below, we will ask the user to enter the employee’s account details, such as password and confirm password. With invalid data, we will get the following error message:

Model Validation using Data Annotation Attributes in ASP.NET Core MVC with Real-time Example

With Valid Data:

With Valid Employee Data, when we click on the Create Employee button, it should create the Employee, i.e., store the Employee information in the database, and then display the following message:

Data Annotation Attributes in ASP.NET Core MVC with Real-time Example

Project Setup:

We are going to work on the same project that we created in our previous article. If you have not yet created the project, please create a new ASP.NET Core Project named DataAnnotationsDemo using the Model View Controller template.

Once you create the Project, Open the Package Manager Console and run the following commands, which are required for Entity Framework Core when working with SQL Server database:

  • Install-Package Microsoft.EntityFrameworkCore
  • Install-Package Microsoft.EntityFrameworkCore.SqlServer
  • Install-Package Microsoft.EntityFrameworkCore.Tools

Once you install the above packages, you can verify the same under the Dependencies\Packages folder, as shown in the below image:

ASP.NET Core Project with EF Core Packages

Defining Models with EF Core

We will use the EF Core Code First approach to define our models. The following are the models we are going to use in our application:

  • Employee (Core information about the employee such as Name, Age, Department)
  • Department (Stores department names)
  • Address (Stores employee addresses)
  • SkillSet (Stores employee skills)
  • JobDetails (Stores employee job-related details)
  • JobTitle (stores employee job titles)
Gender Enumerations

Create a class file named Gender.cs within the Models folder, then copy and paste the following code. This enum defines a set of predefined gender options (Male, Female, Other) to standardize and restrict the values used for the Gender property in other models.

namespace DataAnnotationsDemo.Models
{
    public enum Gender
    {
        Male,
        Female,
        Other
    }
}
Department Model

Create a class file named Department.cs within the Models folder and copy and paste the following code. This model represents the organizational departments within the company.

using System.ComponentModel.DataAnnotations;
namespace DataAnnotationsDemo.Models
{
    public class Department
    {
        [Key]
        public int DepartmentId { get; set; }

        [Required(ErrorMessage = "Department Name is required")]
        [StringLength(50, MinimumLength = 2, ErrorMessage = "Department name should be between 2 and 50 characters")]
        public string Name { get; set; }
    }
}
SkillSet Model

Create a class file named SkillSet.cs within the Models folder and copy and paste the following code. This model captures the various skills that employees can have and forms a many-to-many relationship between Employee and SkillSet to allow multiple employees to have multiple skills and vice versa.

using System.ComponentModel.DataAnnotations;
namespace DataAnnotationsDemo.Models
{
    public class SkillSet
    {
        [Key]
        public int SkillSetId { get; set; }

        [Required(ErrorMessage = "Skill Name is required")]
        [StringLength(50)]
        public string SkillName { get; set; }

        // Navigation property
        public List<Employee> Employees { get; set; }
    }
}
JobTitle Model

Create a class file named JobTitle.cs within the Models folder and copy and paste the following code. This model stores different job titles available within the organization, enabling the association of specific titles with employees.

using System.ComponentModel.DataAnnotations;
namespace DataAnnotationsDemo.Models
{
    public class JobTitle
    {
        [Key]
        public int JobTitleId { get; set; }

        [Required]
        [StringLength(100)]
        public string TitleName { get; set; }
    }
}
Address Model

Create a class file named Address.cs within the Models folder and then copy and paste the following code. This model stores detailed address information for each employee, including street, city, state, and postal code, and establishes a one-to-one relationship with the Employee model.

using System.ComponentModel.DataAnnotations;
namespace DataAnnotationsDemo.Models
{
    public class Address
    {
        [Key]
        public int AddressId { get; set; }

        [Required(ErrorMessage = "Street is required")]
        [StringLength(100, ErrorMessage = "Street cannot exceed 100 characters")]
        public string Street { get; set; }

        [Required(ErrorMessage = "City is required")]
        [StringLength(50, ErrorMessage = "City cannot exceed 50 characters")]
        public string City { get; set; }

        [Required(ErrorMessage = "State is required")]
        [StringLength(50, ErrorMessage = "State cannot exceed 50 characters")]
        public string State { get; set; }

        [Required(ErrorMessage = "Postal Code is required")]
        [RegularExpression(@"^\d{5}(-\d{4})?$|^\d{6}$", ErrorMessage = "Invalid Postal Code")]
        public string PostalCode { get; set; }

        public int EmployeeId { get; set; } //Foreign Key

        // Navigation property
        public Employee Employee { get; set; }
    }
}
JobDetail Model

Create a class file named JobDetail.cs within the Models folder and copy and paste the following code. This model stores detailed job information about an employee, including the job title, department, and salary.

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace DataAnnotationsDemo.Models
{
    public class JobDetail
    {
        [Key]
        public int JobDetailId { get; set; }

        [Required(ErrorMessage = "Job Title is required")]
        public int JobTitleId { get; set; }

        [Required(ErrorMessage = "Department is required")]
        public int DepartmentId { get; set; }
        [Required(ErrorMessage = "Department is required")]
        public int EmployeeId { get; set; }

        [DataType(DataType.Currency)]
        [Range(30000, 200000, ErrorMessage = "Salary must be between 30,000 and 200,000")]
        [Column(TypeName ="decimal(18,2)")]
        public decimal Salary { get; set; }

        // Navigation properties
        public Department Department { get; set; }
        public Employee Employee { get; set; }
        public JobTitle JobTitle { get; set; }
    }
}
Employee Model

Create a class file named Employee.cs within the Models folder, and then copy and paste the following code. This model stores core information about the employee, including personal information, address, job details, associated skills, and account credentials. It establishes relationships with other models like Address, JobDetail, Department, and SkillSet.

using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;
namespace DataAnnotationsDemo.Models
{
    //Creating Uniqe Index on Email Column to avoid Duplicate Emails
    [Index(nameof(Email), Name = "IX_Employees_Unique_Email", IsUnique = true)]
    public class Employee
    {
        [Key]
        public int EmployeeId { get; set; }

        // Basic Information
        [Required(ErrorMessage = "First Name is required")]
        [StringLength(30, MinimumLength = 2, ErrorMessage = "First name should be between 2 and 30 characters")]
        public string FirstName { get; set; }

        [Required(ErrorMessage = "Last Name is required")]
        [StringLength(30, ErrorMessage = "Last name cannot exceed 30 characters")]
        public string LastName { get; set; }

        [Required(ErrorMessage = "Email is required")]
        [EmailAddress(ErrorMessage = "Invalid Email Address")]
        public string Email { get; set; }

        [Required(ErrorMessage = "Date of Birth is required")]
        [DataType(DataType.Date)]
        public DateTime? DateOfBirth { get; set; }

        [Required(ErrorMessage = "Joining Date is required")]
        [DataType(DataType.Date)]
        public DateTime? JoiningDate { get; set; }

        [Required(ErrorMessage = "Gender is required")]
        public Gender Gender { get; set; }

        // Account Information
        [Required(ErrorMessage = "Password is required")]
        [DataType(DataType.Password)]
        [StringLength(100, MinimumLength = 6, ErrorMessage = "Password should be at least 6 characters")]
        public string Password { get; set; }

        // Address Information
        public Address Address { get; set; }

        // Job Details
        public JobDetail JobDetail { get; set; }

        // Skills (Many-to-Many)
        public ICollection<SkillSet> SkillSets { get; set; }
    }
}
Configuring the Database Context

Create a new folder named Data in the project root directory. Then, add a class file named ApplicationDbContext.cs within the Data folder and copy and paste the following code.

using DataAnnotationsDemo.Models;
using Microsoft.EntityFrameworkCore;

namespace DataAnnotationsDemo.Data
{
    public class ApplicationDbContext : DbContext
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            // For ContentType enum
            modelBuilder.Entity<Employee>()
                .Property(c => c.Gender)
                .HasConversion<string>()
                .IsRequired();    // Optional: Specify if the property is required

            // Seeding Departments
            modelBuilder.Entity<Department>().HasData(
                new Department { DepartmentId = 1, Name = "IT" },
                new Department { DepartmentId = 2, Name = "HR" },
                new Department { DepartmentId = 3, Name = "Payroll" },
                new Department { DepartmentId = 4, Name = "Admin" }
            );

            // Seeding SkillSets
            modelBuilder.Entity<SkillSet>().HasData(
                new SkillSet { SkillSetId = 1, SkillName = "Dot Net" },
                new SkillSet { SkillSetId = 2, SkillName = "Java" },
                new SkillSet { SkillSetId = 3, SkillName = "Python" },
                new SkillSet { SkillSetId = 4, SkillName = "PHP" },
                new SkillSet { SkillSetId = 5, SkillName = "Database" }
            );

            // Seed JobTitles
            modelBuilder.Entity<JobTitle>().HasData(
                new JobTitle { JobTitleId = 1, TitleName = "Software Engineer" },
                new JobTitle { JobTitleId = 2, TitleName = "Project Manager" },
                new JobTitle { JobTitleId = 3, TitleName = "Quality Assurance Engineer" },
                new JobTitle { JobTitleId = 4, TitleName = "Business Analyst" },
                new JobTitle { JobTitleId = 5, TitleName = "System Administrator" }
                // Add more job titles as needed
            );
        }

        // DbSets for each model
        public DbSet<Employee> Employees { get; set; }
        public DbSet<Department> Departments { get; set; }
        public DbSet<Address> Addresses { get; set; }
        public DbSet<SkillSet> SkillSets { get; set; }
        public DbSet<JobDetail> JobDetails { get; set; }
        public DbSet<JobTitle> JobTitles { get; set; }
    }
}

The ApplicationDbContext class serves as the bridge between the application and the database using Entity Framework Core. It defines DbSet properties for each model, enabling CRUD operations. The OnModelCreating method configures initial seed data for departments, skill sets, and job titles, manages relationships between these entities, and automatically creates join tables for many-to-many relationships. It is also configured to store an enum named constant (string values) instead of an integer in the database for the Gender column of the Employees table.

Modifying appsettings.json file:

Configure the connection string to your SQL Server database. So, modify the appsettings.json file as follows:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=LAPTOP-6P5NK25R\\SQLSERVER2022DEV;Database=EmployeeManagementDB;Trusted_Connection=True;TrustServerCertificate=True;"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}
Modifying Program.cs Class file:

Register the ApplicationDbContext with the dependency injection container. So, modify the Program class as follows:

using DataAnnotationsDemo.Data;
using Microsoft.EntityFrameworkCore;

namespace DataAnnotationsDemo
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            // Add services to the container.
            builder.Services.AddControllersWithViews();

            // Register ApplicationDbContext with SQL Server provider
            builder.Services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (!app.Environment.IsDevelopment())
            {
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

            app.MapControllerRoute(
                name: "default",
                pattern: "{controller=Employee}/{action=Create}/{id?}");

            app.Run();
        }
    }
}
Generating and Applying Migration:

Open the Package Manager Console and Execute the Add-Migration and Update-Database commands as follows to generate the Migration file and then apply the Migration file to create the database and schemas based on our Models and DbContext class:

Data Annotation Attributes in ASP.NET Core MVC

If you verify the database, you should see the EmployeeManagementDB database with the required tables, as shown in the image below. The EmployeeSkillSet joining table is also automatically created by EF Core to manage the many-to-many relationships between Employees and Skill Sets.

Data Annotation Attributes in ASP.NET Core MVC

Note: You can verify the Departments, JobTitles, and SkillSets table to see the prepopulated seed data inserted while applying the Migration.

Creating the Employee View Model

Create a new folder named ViewModels inside Models and add the following EmployeeViewModel class. The EmployeeViewModel acts as a data transfer object between the Employee model and the views, encapsulating all necessary information for creating and displaying employee data with proper validation. It includes properties for user input, such as personal details, address, job information, selected skills, and account credentials, along with collections for populating dropdowns and checkboxes in the views.

using DataAnnotationsDemo.ValidationAttributes;
using Microsoft.AspNetCore.Mvc.Rendering;
using System.ComponentModel.DataAnnotations;

namespace DataAnnotationsDemo.Models.ViewModels
{
    public class EmployeeViewModel
    {
        // Basic Information
        [Required(ErrorMessage = "First Name is required")]
        [Display(Name = "First Name")]
        [StringLength(30, MinimumLength = 2, ErrorMessage = "First name should be between 2 and 30 characters")]
        public string FirstName { get; set; }

        [Required(ErrorMessage = "Last Name is required")]
        [StringLength(30, ErrorMessage = "Last name cannot exceed 30 characters")]
        [Display(Name = "Last Name")]
        public string LastName { get; set; }

        [Required(ErrorMessage = "Email is required")]
        [EmailAddress(ErrorMessage = "Invalid Email Address")]
        public string Email { get; set; }

        [Required(ErrorMessage = "Date of Birth is required")]
        [DataType(DataType.Date)]
        [Display(Name = "Date Of Birth")]
        public DateTime? DateOfBirth { get; set; }

        [Required(ErrorMessage = "Joining Date is required")]
        [DataType(DataType.Date)]
        [Display(Name = "Joining Date")]
        public DateTime? JoiningDate { get; set; }

        [Required(ErrorMessage = "Gender is required")]
        public Gender? Gender { get; set; }

        // Address Information
        [Required(ErrorMessage = "Street is required")]
        [StringLength(100, ErrorMessage = "Street cannot exceed 100 characters")]
        public string Street { get; set; }

        [Required(ErrorMessage = "City is required")]
        [StringLength(50, ErrorMessage = "City cannot exceed 50 characters")]
        public string City { get; set; }

        [Required(ErrorMessage = "State is required")]
        [StringLength(50, ErrorMessage = "State cannot exceed 50 characters")]
        public string State { get; set; }

        [Required(ErrorMessage = "Postal Code is required")]
        [RegularExpression(@"^\d{5}(-\d{4})?$|^\d{6}$", ErrorMessage = "Invalid Postal Code")]
        [Display(Name = "Postal or Zip Code")]
        public string PostalCode { get; set; }

        // Job Details
        [Required(ErrorMessage = "Job Title is required")]
        [Display(Name = "Job Title")]
        public int SelectedJobTitleId { get; set; }

        [Required(ErrorMessage = "Department is required")]
        [Display(Name = "Department")]
        public int DepartmentId { get; set; }

        [DataType(DataType.Currency)]
        [Range(30000, 200000, ErrorMessage = "Salary must be between 30,000 and 200,000")]
        public decimal Salary { get; set; }

        // Skills
        [Display(Name = "Skills")]
        public List<int> SkillSetIds { get; set; }

        // Account Information
        [Required(ErrorMessage = "Password is required")]
        [DataType(DataType.Password)]
        [StringLength(100, MinimumLength = 6, ErrorMessage = "Password should be at least 6 characters")]
        public string Password { get; set; }

        [Required(ErrorMessage = "Confirm Password is required")]
        [DataType(DataType.Password)]
        [Compare("Password", ErrorMessage = "Passwords do not match")]
        [Display(Name = "Confirm Password")]
        public string ConfirmPassword { get; set; }

        // Lists for Dropdowns and Radio Buttons
        public IEnumerable<SelectListItem>? Departments { get; set; }
        public IEnumerable<SelectListItem>? SkillSets { get; set; }
        public IEnumerable<Gender>? Genders { get; set; }
        public IEnumerable<SelectListItem>? JobTitles { get; set; }
    }
}
Modifying the Employee Controller

Next, create a new EmployeeController or modify the EmployeeController as follows to handle asynchronous operations with proper exception handling. The Employee Controller Manages the data flow between the views and the models. It handles HTTP requests related to employee operations, such as displaying the employee creation form (Create action), processing form submissions to add new employees to the database (Create POST action), and presenting a success page with the newly registered employee’s details (Successful action). The controller also populates necessary dropdown data and manages relationships between entities during employee creation.

using DataAnnotationsDemo.Data;
using DataAnnotationsDemo.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using DataAnnotationsDemo.Models.ViewModels;

namespace DataAnnotationsDemo.Controllers
{
    public class EmployeeController : Controller
    {
        private readonly ApplicationDbContext _context;

        // Injecting the DbContext via constructor
        public EmployeeController(ApplicationDbContext context)
        {
            _context = context;
        }

        // GET: Employees/Create
        public async Task<IActionResult> Create()
        {
            var viewModel = new EmployeeViewModel();
            await PopulateViewModelAsync(viewModel);
            return View(viewModel);
        }

        // POST: Employees/Create
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Create(EmployeeViewModel model)
        {
            if (ModelState.IsValid)
            {
                try
                {
                    // Map ViewModel to Model
                    var employee = new Employee
                    {
                        FirstName = model.FirstName,
                        LastName = model.LastName,
                        Email = model.Email,
                        DateOfBirth = model.DateOfBirth,
                        JoiningDate = model.JoiningDate,
                        Gender = model.Gender.Value,
                        Password = model.Password,
                        
                        // Job Details
                        JobDetail = new JobDetail
                        {
                            JobTitleId = model.SelectedJobTitleId,
                            DepartmentId = model.DepartmentId,
                            Salary = model.Salary
                        },

                        // Address
                        Address = new Address
                        {
                            Street = model.Street,
                            City = model.City,
                            State = model.State,
                            PostalCode = model.PostalCode
                        },

                        // Skills
                        SkillSets = new List<SkillSet>()
                    };

                    // Adding Skills
                    if (model.SkillSetIds != null)
                    {
                        foreach (var skillId in model.SkillSetIds)
                        {
                            employee.SkillSets.Add(new SkillSet
                            {
                                SkillSetId = skillId
                            });
                        }
                    }

                    // Attach selected skills
                    if (model.SkillSetIds != null && model.SkillSetIds.Any())
                    {
                        // Fetch the selected skills from the database
                        var selectedSkills = await _context.SkillSets
                            .Where(s => model.SkillSetIds.Contains(s.SkillSetId))
                            .ToListAsync();

                        // Assign to the Employee's SkillSets
                        employee.SkillSets = selectedSkills;
                    }

                    // Add to DbContext
                    _context.Employees.Add(employee);
                    await _context.SaveChangesAsync();

                    // Redirect to Success page with EmployeeId
                    return RedirectToAction(nameof(Successful), new { id = employee.EmployeeId });
                }
                catch (DbUpdateException ex)
                {
                    // Log the error (uncomment ex variable name and write a log.)
                    ModelState.AddModelError("", "Unable to save changes. " +
                        "Try again, and if the problem persists see your system administrator.");
                }
                catch (Exception ex)
                {
                    // Handle other exceptions
                    ModelState.AddModelError("", $"An error occurred: {ex.Message}");
                }
            }

            // If we reach here, something failed; re-populate dropdowns
            await PopulateViewModelAsync(model);
            return View(model);
        }

        // GET: Employees/Successful
        public async Task<IActionResult> Successful(int id)
        {
            var employee = await _context.Employees
                .Include(e => e.Address)
                .Include(e => e.JobDetail)
                    .ThenInclude(jd => jd.JobTitle)
                .Include(e => e.JobDetail)
                    .ThenInclude(jd => jd.Department)
                .Include(e => e.SkillSets)
                .FirstOrDefaultAsync(e => e.EmployeeId == id);

            if (employee == null)
            {
                return NotFound();
            }

            return View(employee);
        }

        // Private helper method to populate ViewModel lists
        private async Task PopulateViewModelAsync(EmployeeViewModel viewModel)
        {
            viewModel.Departments = await _context.Departments
                .AsNoTracking()
                .Select(d => new SelectListItem
                {
                    Value = d.DepartmentId.ToString(),
                    Text = d.Name
                })
                .ToListAsync();

            viewModel.SkillSets = await _context.SkillSets
                .AsNoTracking()
                .Select(s => new SelectListItem
                {
                    Value = s.SkillSetId.ToString(),
                    Text = s.SkillName
                })
                .ToListAsync();

            viewModel.JobTitles = await _context.JobTitles
                .AsNoTracking()
                .Select(j => new SelectListItem
                {
                    Value = j.JobTitleId.ToString(),
                    Text = j.TitleName
                })
                .ToListAsync();

            viewModel.Genders = Enum.GetValues(typeof(Gender))
                .Cast<Gender>()
                .ToList();
        }
    }
}
Creating Create View

Next, create a view named Create.cshtml within the Views/Employee folder and then copy and paste the following code. The Create View Provides a user interface for creating a new employee. It presents a form that collects all necessary employee information, including personal details, address, job specifics, selected skills, and account credentials. The view uses the EmployeeViewModel to bind data and display validation messages, ensuring that users input valid and complete information before submission.

@model DataAnnotationsDemo.Models.ViewModels.EmployeeViewModel

@{
    ViewData["Title"] = "Create Employee";
}

<!-- Enhanced Heading Section -->
<div class="row my-4">
    <div class="col text-center">
        <h4 class="display-4 fw-bold">
            Add New Employee
        </h4>
        <p class="lead">Please fill out the form below to create a new employee profile.</p>
    </div>
</div>

<div class="container">
    <form asp-action="Create" asp-controller="Employee" method="post">
        <div asp-validation-summary="ModelOnly" class="text-danger mb-3"></div>

        <!-- Basic Information Section -->
        <div class="card mb-4">
            <div class="card-header">
                <h5>Basic Information</h5>
            </div>
            <div class="card-body">
                <div class="row mb-3 align-items-center">
                    <label asp-for="FirstName" class="col-sm-3 col-form-label"></label>
                    <div class="col-sm-9">
                        <input asp-for="FirstName" class="form-control" placeholder="Enter First Name" />
                        <span asp-validation-for="FirstName" class="text-danger"></span>
                    </div>
                </div>

                <div class="row mb-3 align-items-center">
                    <label asp-for="LastName" class="col-sm-3 col-form-label"></label>
                    <div class="col-sm-9">
                        <input asp-for="LastName" class="form-control" placeholder="Enter Last Name" />
                        <span asp-validation-for="LastName" class="text-danger"></span>
                    </div>
                </div>

                <div class="row mb-3 align-items-center">
                    <label asp-for="Email" class="col-sm-3 col-form-label"></label>
                    <div class="col-sm-9">
                        <input asp-for="Email" class="form-control" placeholder="Enter Email" />
                        <span asp-validation-for="Email" class="text-danger"></span>
                    </div>
                </div>

                <div class="row mb-3 align-items-center">
                    <label asp-for="DateOfBirth" class="col-sm-3 col-form-label"></label>
                    <div class="col-sm-9">
                        <input asp-for="DateOfBirth" class="form-control" type="date" />
                        <span asp-validation-for="DateOfBirth" class="text-danger"></span>
                    </div>
                </div>

                <div class="row mb-3 align-items-center">
                    <label asp-for="JoiningDate" class="col-sm-3 col-form-label"></label>
                    <div class="col-sm-9">
                        <input asp-for="JoiningDate" class="form-control" type="date" />
                        <span asp-validation-for="JoiningDate" class="text-danger"></span>
                    </div>
                </div>

                <div class="row mb-3 align-items-center">
                    <label asp-for="Gender" class="col-sm-3 col-form-label"></label>
                    <div class="col-sm-9">
                        @foreach (var gender in Model.Genders)
                        {
                            <div class="form-check form-check-inline">
                                <input class="form-check-input"
                                       type="radio"
                                       name="Gender"
                                       value="@gender"
                                       id="gender_@gender"
                                @(Model.Gender == gender ? "checked" : "") />
                                <label class="form-check-label me-3" for="gender_@gender">@gender</label>
                            </div>
                        }
                        <span asp-validation-for="Gender" class="text-danger"></span>
                    </div>
                </div>
            </div>
        </div>

        <!-- Address Information Section -->
        <div class="card mb-4">
            <div class="card-header">
                <h5>Address Information</h5>
            </div>
            <div class="card-body">
                <div class="row mb-3 align-items-center">
                    <label asp-for="Street" class="col-sm-3 col-form-label"></label>
                    <div class="col-sm-9">
                        <input asp-for="Street" class="form-control" placeholder="Enter Street Address" />
                        <span asp-validation-for="Street" class="text-danger"></span>
                    </div>
                </div>

                <div class="row mb-3 align-items-center">
                    <label asp-for="City" class="col-sm-3 col-form-label"></label>
                    <div class="col-sm-9">
                        <input asp-for="City" class="form-control" placeholder="Enter City" />
                        <span asp-validation-for="City" class="text-danger"></span>
                    </div>
                </div>

                <div class="row mb-3 align-items-center">
                    <label asp-for="State" class="col-sm-3 col-form-label"></label>
                    <div class="col-sm-9">
                        <input asp-for="State" class="form-control" placeholder="Enter State" />
                        <span asp-validation-for="State" class="text-danger"></span>
                    </div>
                </div>

                <div class="row mb-3 align-items-center">
                    <label asp-for="PostalCode" class="col-sm-3 col-form-label"></label>
                    <div class="col-sm-9">
                        <input asp-for="PostalCode" class="form-control" placeholder="Enter Postal Code" />
                        <span asp-validation-for="PostalCode" class="text-danger"></span>
                    </div>
                </div>
            </div>
        </div>

        <!-- Job Details Section -->
        <div class="card mb-4">
            <div class="card-header">
                <h5>Job Details</h5>
            </div>
            <div class="card-body">
                <!-- Job Title Dropdown -->
                <div class="row mb-3 align-items-center">
                    <label asp-for="SelectedJobTitleId" class="col-sm-3 col-form-label"></label>
                    <div class="col-sm-9">
                        <select asp-for="SelectedJobTitleId" class="form-select">
                            <option value="">-- Select Job Title --</option>
                            @foreach (var jobTitle in Model.JobTitles)
                            {
                                <option value="@jobTitle.Value">@jobTitle.Text</option>
                            }
                        </select>
                        <span asp-validation-for="SelectedJobTitleId" class="text-danger"></span>
                    </div>
                </div>

                <div class="row mb-3 align-items-center">
                    <label asp-for="DepartmentId" class="col-sm-3 col-form-label"></label>
                    <div class="col-sm-9">
                        <select asp-for="DepartmentId" class="form-select">
                            <option value="">-- Select Department --</option>
                            @foreach (var dept in Model.Departments)
                            {
                                <option value="@dept.Value">@dept.Text</option>
                            }
                        </select>
                        <span asp-validation-for="DepartmentId" class="text-danger"></span>
                    </div>
                </div>

                <div class="row mb-3 align-items-center">
                    <label asp-for="Salary" class="col-sm-3 col-form-label"></label>
                    <div class="col-sm-9">
                        <input asp-for="Salary" class="form-control" placeholder="Enter Salary" />
                        <span asp-validation-for="Salary" class="text-danger"></span>
                    </div>
                </div>

                <div class="row mb-3 align-items-center">
                    <label asp-for="SkillSetIds" class="col-sm-3 col-form-label"></label>
                    <div class="col-sm-9">
                        @foreach (var skill in Model.SkillSets)
                        {
                            <div class="form-check form-check-inline">
                                <input class="form-check-input"
                                       type="checkbox"
                                       name="SkillSetIds"
                                       value="@skill.Value"
                                       id="skill_@skill.Value"
                                @(Model.SkillSetIds != null && Model.SkillSetIds.Contains(Convert.ToInt32(skill.Value)) ? "checked" : "") />
                                <label class="form-check-label me-3" for="skill_@skill.Value">@skill.Text</label>
                            </div>
                        }
                        <span asp-validation-for="SkillSetIds" class="text-danger"></span>
                    </div>
                </div>
            </div>
        </div>

        <!-- Account Information Section -->
        <div class="card mb-4">
            <div class="card-header">
                <h5>Account Information</h5>
            </div>
            <div class="card-body">
                <div class="row mb-3 align-items-center">
                    <label asp-for="Password" class="col-sm-3 col-form-label"></label>
                    <div class="col-sm-9">
                        <input asp-for="Password" class="form-control" placeholder="Enter Password" />
                        <span asp-validation-for="Password" class="text-danger"></span>
                    </div>
                </div>

                <div class="row mb-3 align-items-center">
                    <label asp-for="ConfirmPassword" class="col-sm-3 col-form-label"></label>
                    <div class="col-sm-9">
                        <input asp-for="ConfirmPassword" class="form-control" placeholder="Confirm Password" />
                        <span asp-validation-for="ConfirmPassword" class="text-danger"></span>
                    </div>
                </div>
            </div>
        </div>

        <!-- Submit Button -->
        <div class="d-flex justify-content-end">
            <button type="submit" class="btn btn-primary">Create Employee</button>
        </div>
    </form>
</div>

@section Scripts {
    @{
        await Html.RenderPartialAsync("_ValidationScriptsPartial");
    }
}
Creating the Success View

Next, create a view named Success.cshtml within the Views/Employee folder and then copy and paste the following code. The Success View Displays a confirmation page upon successful employee registration. It welcomes the newly registered employee by name and showcases their submitted information in organized sections, such as Basic Information, Address, Job Details, and Skills. The view aims to provide immediate feedback and a summary of the entered data, enhancing the user experience by confirming successful operations.

@model DataAnnotationsDemo.Models.Employee

@{
    ViewData["Title"] = "Registration Successful";
}

<div class="container mt-5">
    <div class="card shadow-sm">
        <div class="card-header bg-success text-white">
            <h2 class="card-title mb-0">Welcome, @Model.FirstName @Model.LastName!</h2>
        </div>
        <div class="card-body">
            <p class="lead">Your registration was successful. Below are your details:</p>
            <hr />
            <div class="row mb-3">
                <div class="col-md-6">
                    <h5>Basic Information</h5>
                    <ul class="list-group list-group-flush">
                        <li class="list-group-item"><strong>Name:</strong> @Model.FirstName @Model.LastName</li>
                        <li class="list-group-item"><strong>Email:</strong> @Model.Email</li>
                        <li class="list-group-item"><strong>Date of Birth:</strong> @Model.DateOfBirth?.ToString("MMMM dd, yyyy")</li>
                        <li class="list-group-item"><strong>Gender:</strong> @Model.Gender</li>
                    </ul>
                </div>
                <div class="col-md-6">
                    <h5>Address</h5>
                    <ul class="list-group list-group-flush">
                        <li class="list-group-item"><strong>Street:</strong> @Model.Address.Street</li>
                        <li class="list-group-item"><strong>City:</strong> @Model.Address.City</li>
                        <li class="list-group-item"><strong>State:</strong> @Model.Address.State</li>
                        <li class="list-group-item"><strong>Postal Code:</strong> @Model.Address.PostalCode</li>
                    </ul>
                </div>
            </div>

            <div class="row mb-3">
                <div class="col-md-6">
                    <h5>Job Details</h5>
                    <ul class="list-group list-group-flush">
                        <li class="list-group-item"><strong>Job Title:</strong> @Model.JobDetail.JobTitle.TitleName</li>
                        <li class="list-group-item"><strong>Department:</strong> @Model.JobDetail.Department.Name</li>
                        <li class="list-group-item"><strong>Salary:</strong> @Model.JobDetail.Salary.ToString("C")</li>
                    </ul>
                </div>
                <div class="col-md-6">
                    <h5>Skills</h5>
                    <ul class="list-group list-group-flush">
                        @if (Model.SkillSets != null && Model.SkillSets.Any())
                        {
                            foreach (var skill in Model.SkillSets)
                            {
                                <li class="list-group-item">@skill.SkillName</li>
                            }
                        }
                        else
                        {
                            <li class="list-group-item">No skills selected.</li>
                        }
                    </ul>
                </div>
            </div>
        </div>
    </div>
</div>

With the above changes in place, run the application and test it, and it should work as expected. In the DateOfJoining and DateOfBirth properties, we have applied a built-in Attribute to ensure the date is required and format it correctly as a date. But if you want to enforce that the date of joining should not be a future date and the DateOfBirth should ensure the employee’s age is between 18 and 60, then we need to create a custom validation attribute, which we will discuss in our next article.

In the next article, I will discuss How to Create Custom Data Annotations in ASP.NET Core MVC Applications with Real-Time Examples. In this article, I try to explain Data Annotation Attributes in ASP.NET Core MVC with Examples. I hope you enjoy this Data Annotation Attributes in ASP.NET Core MVC article.

Leave a Reply

Your email address will not be published. Required fields are marked *