ASP.NET Core 2.0 Prevent Duplicate Entries
You can certainly check for duplicate entries from the code side, but you will of course need to run a query against the database in order to achieve this. From your posted code, you already do something similar in EmployeesController.Edit
:
Employee employee = await _context.Employees.SingleOrDefaultAsync(
m => m.EmployeeID == id);
if (employee == null)
{
return NotFound();
}
You could take this approach and apply a variation to EmployeesController.Create
. e.g.:
Employee existingEmployee = await _context.Employees.SingleOrDefaultAsync(
m => m.FirstName == employee.FirstName && m.LastName == employee.LastName);
if (existingEmployee != null)
{
// The employee already exists.
// Do whatever you need to do - This is just an example.
ModelState.AddModelError(string.Empty, "This employee already exists.");
employee.Departments = _context.Departments.ToList();
employee.Appointments = _context.Appointments.ToList();
return View(employee);
}
// Your existing code for creating a new employee.
The expression I've used in SingleOrDefaultAsync
is just an example: You will need to decide what makes an employee unique according to your own requirements.
To see the error message added in the controller code I've shown, you'll need to update the Create View:
@using (Html.BeginForm("Create", "Employees"))
{
@Html.ValidationSummary(true)
...
Brian
Updated on June 05, 2022Comments
-
Brian almost 2 years
I'm very new to ASP.NET Core MVC. I'm using ASP.NET Core 2.0, C#, EntityFrameworkCore Code First and SQL Server 2016. I managed to get my CRUD Operations to work as needed.
However, I need help on preventing users from registering more than once on my form. If possible I would like to check/prevent the duplicate entries from within the code on the Controller side and not within the Database.
I'm attaching my code that I have now if someone could possible assist. Thank you in advance!
Models
public class Employee { public int EmployeeID { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public int DepartmentID { get; set; } public Department Department { get; set; } public int AppointmentID { get; set; } public Appointment Appointment { get; set; } } public class Department { public int DepartmentID { get; set; } public string Name { get; set; } public ICollection<Employee> Employees { get; set; } } public class Appointment { public int AppointmentID { get; set; } public string TimeSlot { get; set; } public ICollection<Employee> Employees { get; set; } }
ViewModels
public class EmployeeFormVM { public int EmployeeID { get; set; } [Required(ErrorMessage = "Please enter your First Name")] [Display(Name = "First Name")] [StringLength(50)] public string FirstName { get; set; } [Required(ErrorMessage = "Please enter your Last Name")] [Display(Name = "Last Name")] [StringLength(50)] public string LastName { get; set; } [Required(ErrorMessage = "Please select your Department")] [Display(Name = "Department")] public int DepartmentID { get; set; } public IEnumerable<Department> Departments { get; set; } [Required(ErrorMessage = "Please select your Appointment")] [Display(Name = "Appointment")] public int AppointmentID { get; set; } public IEnumerable<Appointment> Appointments { get; set; } }
DbContext
public class WinTenDbContext : DbContext { public WinTenDbContext(DbContextOptions<WinTenDbContext> options) : base(options) { } public DbSet<Employee> Employees { get; set; } public DbSet<Department> Departments { get; set; } public DbSet<Appointment> Appointments { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Employee>() .HasKey(e => e.EmployeeID); modelBuilder.Entity<Employee>() .Property(e => e.FirstName) .HasColumnType("varchar(50)") .HasMaxLength(50) .IsRequired(); modelBuilder.Entity<Employee>() .Property(e => e.LastName) .HasColumnType("varchar(50)") .HasMaxLength(50) .IsRequired(); modelBuilder.Entity<Department>() .HasKey(d => d.DepartmentID); modelBuilder.Entity<Department>() .Property(d => d.Name) .HasColumnType("varchar(50)") .HasMaxLength(50); modelBuilder.Entity<Appointment>() .HasKey(a => a.AppointmentID); modelBuilder.Entity<Appointment>() .Property(a => a.TimeSlot) .HasColumnType("varchar(50)") .HasMaxLength(50); } }
EmployeesController
public class EmployeesController : Controller { private readonly WinTenDbContext _context; public EmployeesController(WinTenDbContext context) { _context = context; } // GET: Employees public async Task<IActionResult> Index() { //return View(await _context.Employees.ToListAsync()); var webAppDbContext = _context.Employees.Include(d => d.Department).Include(a => a.Appointment); return View(await webAppDbContext.ToListAsync()); } // GET: Employees/Details/5 public async Task<IActionResult> Details(int? id) { if (id == null) { return NotFound(); } var employee = await _context.Employees .SingleOrDefaultAsync(m => m.EmployeeID == id); if (employee == null) { return NotFound(); } return View(employee); } // GET: Employees/Create public IActionResult Create() { var departments = _context.Departments.ToList(); var appointments = _context.Appointments.ToList(); var viewModel = new EmployeeFormVM { Departments = departments, Appointments = appointments }; return View(viewModel); } // POST: Employees/Create // To protect from overposting attacks, please enable the specific properties you want to bind to, for // more details see http://go.microsoft.com/fwlink/?LinkId=317598. [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Create(EmployeeFormVM employee) { if (ModelState.IsValid) { var emp = new Employee(); { emp.FirstName = employee.FirstName; emp.LastName = employee.LastName; emp.DepartmentID = employee.DepartmentID; emp.AppointmentID = employee.AppointmentID; } _context.Add(emp); await _context.SaveChangesAsync(); return RedirectToAction(nameof(Index)); } else { employee.Departments = _context.Departments.ToList(); employee.Appointments = _context.Appointments.ToList(); return View(employee); } } // GET: Employees/Edit/5 public async Task<IActionResult> Edit(int? id) { if (id == null) { return NotFound(); } var employeevm = new EmployeeFormVM(); { Employee employee = await _context.Employees.SingleOrDefaultAsync(m => m.EmployeeID == id); if (employee == null) { return NotFound(); } employeevm.EmployeeID = employee.EmployeeID; employeevm.FirstName = employee.FirstName; employeevm.LastName = employee.LastName; // Retrieve list of Departments var departments = _context.Departments.ToList(); employeevm.Departments = departments; // Set the selected department employeevm.DepartmentID = employee.DepartmentID; // Retrieve list of Appointments var appointments = _context.Appointments.ToList(); employeevm.Appointments = appointments; // Set the selected department employeevm.AppointmentID = employee.AppointmentID; } return View(employeevm); } // POST: Employees/Edit/5 // To protect from overposting attacks, please enable the specific properties you want to bind to, for // more details see http://go.microsoft.com/fwlink/?LinkId=317598. [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Edit(EmployeeFormVM vmEdit) { if (ModelState.IsValid) { Employee employee = _context.Employees.SingleOrDefault(e => e.EmployeeID == vmEdit.EmployeeID); if (employee == null) { return NotFound(); } employee.FirstName = vmEdit.FirstName; employee.LastName = vmEdit.LastName; employee.DepartmentID = vmEdit.DepartmentID; employee.AppointmentID = vmEdit.AppointmentID; try { _context.Update(employee); await _context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException) { if (!EmployeeExists(vmEdit.EmployeeID)) { return NotFound(); } else { throw; } } return RedirectToAction(nameof(Index)); } return View(vmEdit); } // GET: Employees/Delete/5 public async Task<IActionResult> Delete(int? id) { if (id == null) { return NotFound(); } var employee = await _context.Employees .SingleOrDefaultAsync(m => m.EmployeeID == id); if (employee == null) { return NotFound(); } return View(employee); } // POST: Employees/Delete/5 [HttpPost, ActionName("Delete")] [ValidateAntiForgeryToken] public async Task<IActionResult> DeleteConfirmed(int id) { var employee = await _context.Employees.SingleOrDefaultAsync(m => m.EmployeeID == id); _context.Employees.Remove(employee); await _context.SaveChangesAsync(); return RedirectToAction(nameof(Index)); } private bool EmployeeExists(int id) { return _context.Employees.Any(e => e.EmployeeID == id); } }
Create View
@using (Html.BeginForm("Create", "Employees")) { <div class="form-group"> @Html.LabelFor(e => e.FirstName) @Html.TextBoxFor(e => e.FirstName, new { @class = "form-control" }) @Html.ValidationMessageFor(e => e.FirstName) </div> <div class="form-group"> @Html.LabelFor(e => e.LastName) @Html.TextBoxFor(e => e.LastName, new { @class = "form-control" }) @Html.ValidationMessageFor(e => e.LastName) </div> <div class="form-group"> @Html.LabelFor(d => d.DepartmentID) @Html.DropDownListFor(d => d.DepartmentID, new SelectList(Model.Departments, "DepartmentID", "Name"), "", new { @class = "form-control" }) @Html.ValidationMessageFor(d => d.DepartmentID) </div> <div class="form-group"> @Html.LabelFor(a => a.AppointmentID) @Html.DropDownListFor(a => a.AppointmentID, new SelectList(Model.Appointments, "AppointmentID", "TimeSlot"), "", new { @class = "form-control" }) @Html.ValidationMessageFor(a => a.AppointmentID) </div> <div class="form-group"> <button type="submit" class="btn btn-primary">Submit</button> </div> }
-
Brian over 6 yearsThank you for replying back. Were should I implement what you supplied within my original posted code? I tried to put what you supplied inside of the [HttpPost]Create if(ModelState.IsValid) code but when I try to enter a duplicate first and last name I get this error ArgumentNullException: Value cannot be null. Parameter name: items on this line of code @Html.DropDownListFor(d => d.DepartmentID, new SelectList(Model.Departments, "DepartmentID", "Name"), "", new { @class = "form-control" })
-
Kirk Larkin over 6 yearsI've edited the answer to address your specific problem. You'll want to consider restructuring the function a little to remove the duplication this adds but I think that's outside the scope of your specific question here.
-
Brian over 6 yearsThank you, that worked. However, I didn't see the error message This employee already exists. when I tried to enter a duplicate entry, is that something I have to add on the Create View? Also, is there a reason I have to restructure the function that you mentioned.
-
Kirk Larkin over 6 yearsAnswer updated. You don't need to restructure the function - it's just a suggestion to consider the fact that you have duplication of the code that populates the
Departments
andAppointments
lists. -
Diomedes over 6 yearsI recommend you use .FirstOrDefaultAsync instead of SingleOrDefaultAsync, specially if the database already has data and there is the possibility that a duplicate already exists. In this scenario, SingleOrDefaultAsync would throw and error but FirstOrDefaultAsync works as intended. If it is a clean project, then SingleOrDefaultAsync should be ok, but still, Single is safer.