ASP.NET Core 2.0 Предотвращение дублирования записей

Я очень новичок в ASP.NET Core MVC. Я использую ASP.NET Core 2.0, C#, EntityFrameworkCore Code First и SQL Server 2016. Мне удалось заставить мои операции CRUD работать по мере необходимости.

Однако мне нужна помощь, чтобы запретить пользователям регистрироваться в моей форме более одного раза. Если возможно, я хотел бы проверить/предотвратить дублирование записей из кода на стороне контроллера, а не из базы данных.

Я прилагаю свой код, который у меня есть сейчас, если кто-то может помочь. Заранее спасибо!

Модели

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; }
}

Модели просмотра

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; }
}

Дбконтекст

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);
    }
}

Контроллер сотрудников

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);
    }
}

Создать представление

@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>
    }

person Brian    schedule 26.09.2017    source источник


Ответы (1)


Вы, конечно, можете проверить наличие повторяющихся записей со стороны кода, но вам, конечно, потребуется выполнить запрос к базе данных, чтобы добиться этого. Из вашего опубликованного кода вы уже делаете что-то подобное в EmployeesController.Edit:

Employee employee = await _context.Employees.SingleOrDefaultAsync(
    m => m.EmployeeID == id);

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

Вы можете воспользоваться этим подходом и применить вариант к EmployeesController.Create. например.:

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.

Выражение, которое я использовал в SingleOrDefaultAsync, является просто примером: вам нужно будет решить, что делает сотрудника уникальным, в соответствии с вашими собственными требованиями.

Чтобы увидеть сообщение об ошибке, добавленное в код контроллера, который я показал, вам нужно обновить Создать представление:

@using (Html.BeginForm("Create", "Employees"))
{
    @Html.ValidationSummary(true)
    ...
person Kirk Larkin    schedule 26.09.2017
comment
Спасибо, что ответили. Должен ли я реализовать то, что вы предоставили, в моем исходном опубликованном коде? Я попытался поместить то, что вы предоставили, в код [HttpPost]Create if(ModelState.IsValid), но когда я пытаюсь ввести повторяющиеся имя и фамилию, я получаю эту ошибку ArgumentNullException: Value не может быть нулевым. Имя параметра: items в этой строке кода @Html.DropDownListFor(d => d.DepartmentID, new SelectList(Model.Departments, DepartmentID, Name), , new { @class = form-control } ) - person Brian; 27.09.2017
comment
Я отредактировал ответ, чтобы решить вашу конкретную проблему. Вы захотите немного изменить структуру функции, чтобы удалить дублирование, которое это добавляет, но я думаю, что это выходит за рамки вашего конкретного вопроса здесь. - person Kirk Larkin; 27.09.2017
comment
Спасибо, это сработало. Однако я не видел сообщения об ошибке Этот сотрудник уже существует, когда я пытался ввести повторяющуюся запись, нужно ли мне что-то добавить в представление «Создать»? Кроме того, есть ли причина, по которой я должен реструктурировать функцию, о которой вы упомянули. - person Brian; 27.09.2017
comment
Ответ обновлен. Вам не нужно реструктурировать функцию — это просто предложение принять во внимание тот факт, что у вас есть дублирование кода, заполняющего списки Departments и Appointments. - person Kirk Larkin; 27.09.2017
comment
Я рекомендую вам использовать .FirstOrDefaultAsync вместо SingleOrDefaultAsync, особенно если в базе данных уже есть данные и существует вероятность того, что дубликат уже существует. В этом случае SingleOrDefaultAsync выдаст ошибку, но FirstOrDefaultAsync работает так, как предполагалось. Если это чистый проект, тогда SingleOrDefaultAsync должно быть нормально, но все же Single безопаснее. - person Diomedes; 18.12.2017