ASP.NET Core 2.0 Prevent Duplicate Entries - c#

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

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)
...

Related

How to Create the Edit View that gets CheckBoxes from a List stored in DB?

Need to create the edit View with selected Checkboxes from createView
public class CustomerTypeViewModel
{
[Required]
public int? Id { get; set; }
public string Description { get; set; }
[Display(Name = "Please select the Type")]
public bool Selected { get; set; }
}
CustomerView when i should load a list of CustomerTypeViewModel
public List<CustomerTypeViewModel> SelectedCustomerTypes { get; set; }
private List<CustomerTypeViewModel> selectionList = new List<CustomerTypeViewModel>();
public CustomerViewModel()
{
SelectedCustomerTypes = new List<CustomerTypeViewModel>();
}
public void SetCustomerTypeViewModel(IEnumerable<CustomerTypeViewModel> selected, IEnumerable<CustomerTypeViewModel> all)
{
SelectedCustomerTypes.Clear();
foreach (var item in all)
{
SelectedCustomerTypes.Add(item);
}
foreach (var item in selected)
{
SelectedCustomerTypes.FirstOrDefault(x => x.Description == item.Description).Selected = true;
}
}
public List<CustomerTypeViewModel> GetTipi()
{
return selectionList;
}
In controller i should call a method that get customertypes from manager
public CustomerTypeViewModel GetCustomerType(int? customerId)
{
var query = "SELECT * FROM CustomerType where Id = #Id";
return context.GetObject<CustomerTypeViewModel>(query, new { Id = customerId });
}
Now in the Edit at the Controller
[AuthorizeRoles(RoleNames.CanEditCustomer)]
public ActionResult Edit(int? id, int? customerId)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var customer = customerManager.Get(customerId);
var vm = new CustomerViewModel();
vm.SetCustomerTypeViewModel(new List<CustomerTypeViewModel>(), customerTypeManager.GetAll());
if (customer == null)
{
return HttpNotFound();
}
return View(customer);
}
I was told to create a method in manager GetCustomerType(customerId)
<-- this is the id from db table where checkboxes are saved and this method load a list of CustomerTypeViewModel
Now Edit View
<div class="form-group">
#{
for (int i = 0; i < Model.SelectedCustomerTypes.Count(); i++)
{
<div class="col-md-10">
#Html.Label(Model.SelectedCustomerTypes[i].Description, new { #class = "control-label col-md-2" })
#Html.CheckBoxFor(model => model.SelectedCustomerTypes[i].Selected, new { #checked = "checked" })
#Html.HiddenFor(model => model.SelectedCustomerTypes[i].Id, new { data_val = false })
#Html.HiddenFor(model => model.SelectedCustomerTypes[i].Description, new { data_val = false })
</div>
}
}
</div>
You need to fix the GetCustomerType method in your controller. Try this:
public IActionResult GetCostumerType(int? id)
{
if (id == null)
{
return NotFound();
}
var test = from custom in _yourContext.CustomerType
.Where(p => p.CustomerType.Id == customerId)
select custom;
//change _yourContext with your context variable
if (test == null)
{
return NotFound();
}
return View(test);
}
This way you are getting the custom types associated with the id passed in the method.

c# MVC 6 dotnet core select with selected option not selecting

I have a simple select with an option selected from the model that is loaded.
To start here is my simple model
public class invoice
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public invoice_state invoice_state { get; set; }
}
public class invoice_state
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public string alias { get; set; }
public List<invoice> invoices { get; set; }
[Display(Name = "title")]
public string title { get; set; }
}
Here is what I have that works in one view.
in controller:
public IActionResult Create()
{
string state = "start_finish";
ViewBag.State = state;
var states = _context.invoice_state.Select(c => new {
id = c.ID,
title = c.title
}).ToList();
ViewBag.States = new SelectList(states, "id", "title", _context.invoice_state.Where(e => e.alias == state).FirstOrDefault().ID);
return View();
}
in the view
#Html.DropDownList("invoice_state", (SelectList)ViewBag.States, "--Select One--")
That works fine, the option is selected... but on my edit view which is set up mostly the same is not working.
in controller:
public async Task<IActionResult> Edit(int? id)
{
var invoice = await _context.invoice
.Include(_item => _item.invoice_state).SingleOrDefaultAsync(m => m.ID == id);
if (invoice == null)
{
return NotFound();
}
string state = "start_finish"; // as a default
var states = _context.invoice_state.Select(c => new { id = c.ID, title = c.title }).ToList();
if (null != invoice.invoice_state)
{
ViewBag.State = invoice.invoice_state.alias;
}
else
{
ViewBag.State = state;
}
ViewBag.States = new SelectList(states, "id", "title", _context.invoice_state.Where(e => e.alias == state).FirstOrDefault());
return View(invoice);
}
and in the edit view
#Html.DropDownList("invoice_state", (SelectList)ViewBag.States, "--Select One--")
I have read all over the place and can't find a simple answer that isn't wire up more files, and even those haven't helped get me to the need. I have tried to force it too and it is not working, but figured it was worth placing here too.
ViewBag.States = _context
.invoice_state
.Select(c => new SelectListItem {
Value = c.ID.ToString(),
Text = c.title
})
.Select(l => new SelectListItem {
Selected = (l.Value == invoice.invoice_state.ID.ToString()),
Text = l.Text,
Value = l.Value
});
but only get 'Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable<Microsoft.AspNetCore.Mvc.Rendering.SelectListItem>' to 'Microsoft.AspNetCore.Mvc.Rendering.SelectList' or the list version of the error if i add .ToList() on the select like before.
Some people have suggested to set the selected value, and as i read it it would be like,
if (null != invoice.invoice_state)
{
ViewBag.invoice_stateID = invoice.invoice_state.ID;
}
else
{
ViewBag.invoice_stateID = _context.invoice_state.Where(e => e.alias == "start_finish").FirstOrDefault().ID;
}
if i use
<select asp-for="invoice_state" asp-items="#ViewBag.States" >
<option>Please select one</option>
</select>
It doesn't work either, see the list but nothing selected. Last note, if I select it and submit it, it does set the value in the database, just when I get back to the edit it again fails to again select anything.
Also to be clear there is the data
which came from
<dt>
#Html.DisplayNameFor(model => model.invoice_state)
</dt>
<dd>
#Html.DisplayFor(model => model.invoice_state)
</dd>
#Stephen Muecke is right, the ticket is the ViewModel. Not a fan fully here but I'll simmer. The solution is not just getting it to show, you need to save is too. Here is the whole of it.
ViewModel
namespace project_name.Models.InvoiceViewModel
{
public class EditInvoiceViewModel
{
public int invoice_stateID { get; set; }
public invoice invoice { get; set; }
public List<SelectListItem> States { set; get; }
}
}
edit in action on controller
public async Task<IActionResult> Edit(int? id)
{
var invoice = await _context.invoice
.Include(_item => _item.invoice_state).SingleOrDefaultAsync(m => m.ID == id);
string state = "start_finish"; // as a default
var states = _context.invoice_state.Select(c => new { id = c.ID, title = c.title }).ToList();
if (null != invoice.invoice_state)
{
ViewBag.State = invoice.invoice_state.alias;
}
else
{
ViewBag.State = state;
}
var vm = new EditInvoiceViewModel();
vm.invoice = invoice;
vm.States = _context
.invoice_state
.Select(c => new SelectListItem
{
Value = c.ID.ToString(),
Text = c.title
})
.ToList();
if (null != invoice.invoice_state)
{
vm.invoice_stateID = invoice.invoice_state.ID;
} else {
vm.invoice_stateID = _context.invoice_state.Where(e => e.alias == "start_finish").FirstOrDefault().ID;
}
return View(vm);
}
to save in action on controller
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(EditInvoiceViewModel model)
{
if (ModelState.IsValid)
{
try
{
model.invoice.creator = await GetCurrentUserAsync();
model.invoice.invoice_state = await _context.invoice_state.SingleOrDefaultAsync(m => m.ID == model.invoice_stateID);
_context.Update(model.invoice);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!invoiceExists(model.invoice.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction("Index");
}
return View(model);
}
And in the view
<div class="form-group">
<label asp-for="#Model.invoice_stateID" class="col-md-2 control-label"></label>
<select asp-for="#Model.invoice_stateID" asp-items="#Model.States" >
<option>Please select one</option>
</select>
</div>
The thing I guess I was having a hard time with is all supporting questions that are like this, and all the blogs focus on the display, but i still need to save it, and that was not clear. I had to skip the [bind()] but I'll get back to that.

Can't get a property from other class even with the navigation property configured

i'm getting a issue in my program.
In the Index View from the "Emprestimo" controller i'm trying to get the name from the book from the "Livro" class, but when i put the following it only gives me a blank space:
#Html.DisplayFor(modelItem => item.Livro.Titulo)
I Have a foreign key from "Livro" (book) inside the "Emprestimo", and when i put the following, i get the ID from the book, but far i know, i can't put a string as FK:
#Html.DisplayFor(modelItem => item.LivroId)
I tried some variations but i couldn't get the expected result. It's not a list inside the "Emprestimo", because in one loan can exist only one book.
Here's the complete code and the github link from the project if you want to take a better look:
https://github.com/KaioMartins/PersonalLibrary
Usuario.cs
namespace PersonalLibrary.Models
{
public class Usuario
{
public int UsuarioId { get; set; }
[Display(Name = "Usuário")]
public string Nome { get; set; }
[Display(Name = "E-mail")]
public string Email { get; set; }
[Display(Name = "Senha")]
public string Senha { get; set; }
public virtual List<Livro> Livros { get; set; }
public virtual List<Emprestimo> Emprestimos { get; set; }
public virtual List<Autor> Autor { get; set; }
}
}
Livro.cs
namespace PersonalLibrary.Models
{
public class Livro
{
public int LivroId { get; set; }
[Required(ErrorMessage = "Digite o nome do Livro")]
[MaxLength(60)]
[Display(Name = "Livro")]
public string Titulo { get; set; }
[Required(ErrorMessage = "Digite o ISBN")]
[MaxLength(60)]
[Display(Name = "ISBN")]
public string ISBN { get; set; }
[Required(ErrorMessage = "Digite a Data da Compra do Livro")]
[Display(Name = "Data da Compra")]
public DateTime DataCompra { get; set; }
[Display(Name = "Leitura Concluída")]
public Boolean StatusLido { get; set; }
[Required(ErrorMessage = "Cadastre um Autor antes de Cadastrar um Livro")]
[Display(Name = "Autor")]
public int AutorId { get; set; }
[ForeignKey("AutorId")]
public virtual Autor Autor { get; set; }
[Display(Name = "Usuário")]
public int UsuarioId { get; set; }
[ForeignKey("UsuarioId")]
public virtual Usuario Usuario { get; set; }
}
}
Emprestimo.cs
namespace PersonalLibrary.Models
{
public class Emprestimo
{
public int EmprestimoId { get; set; }
[Display(Name = "Emprestado")]
public Boolean Emprestado { get; set; }
[Display(Name = "Emprestado para: ")]
public string PessoaEmprestimo { get; set; }
[Display(Name = "Data do Empréstimo")]
public DateTime DataEmprestimo { get; set; }
[Display(Name = "Data da Devolução")]
public DateTime DataDevolucao { get; set; }
[Display(Name = "Livro")]
public int LivroId { get; set; }
[ForeignKey("LivroId")]
public virtual Livro Livro { get; set; }
[Display(Name = "Usuário")]
public int UsuarioId { get; set; }
[ForeignKey("UsuarioId")]
public virtual Usuario Usuario { get; set; }
}
}
EmprestimoController.cs
namespace PersonalLibrary.Controllers
{
public class EmprestimoController : Controller
{
private LibraryContext db = new LibraryContext();
// GET: Emprestimo
[AuthFilter]
public ActionResult Index()
{
using(LibraryContext ctx = new LibraryContext())
{
Usuario user = (Usuario)Session["usuario"];
int id = user.UsuarioId;
List<Emprestimo> lista = ctx.Emprestimo.Where(c => c.UsuarioId == id).ToList();
return View(lista);
}
//var emprestimo = db.Emprestimo.Include(e => e.Titulo).Include(e => e.Usuario);
//return View(emprestimo.ToList());
}
// GET: Emprestimo/Details/5
[AuthFilter]
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Emprestimo emprestimo = db.Emprestimo.Find(id);
if (emprestimo == null)
{
return HttpNotFound();
}
return View(emprestimo);
}
// GET: Emprestimo/Create
[AuthFilter]
public ActionResult Create()
{
using (LibraryContext ctx = new LibraryContext())
{
Usuario u = (Usuario)Session["usuario"];
ViewBag.LivroId = new SelectList(db.Livro.Where(c => c.UsuarioId == u.UsuarioId), "LivroId", "Titulo");
return View();
}
}
// POST: Emprestimo/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]
[AuthFilter]
public ActionResult Create([Bind(Include = "EmprestimoId,Emprestado,PessoaEmprestimo,DataEmprestimo,DataDevolucao,LivroId,UsuarioId")] Emprestimo emprestimo)
{
using (LibraryContext ctx = new LibraryContext())
{
if (ModelState.IsValid)
{
Usuario u = Session["usuario"] as Usuario;
emprestimo.UsuarioId = u.UsuarioId;
db.Emprestimo.Add(emprestimo);
db.SaveChanges();
ViewBag.LivroId = new SelectList(ctx.Usuario.Where(c => c.UsuarioId == u.UsuarioId));
return RedirectToAction("Index");
}
//ViewBag.LivroId = new SelectList(db.Livro, "LivroId", "Titulo", emprestimo.LivroId);
return View(emprestimo);
}
}
// GET: Emprestimo/Edit/5
[AuthFilter]
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Emprestimo emprestimo = db.Emprestimo.Find(id);
if (emprestimo == null)
{
return HttpNotFound();
}
using (LibraryContext ctx = new LibraryContext())
{
Usuario u = (Usuario)Session["usuario"];
ViewBag.LivroId = new SelectList(db.Livro.Where(c => c.UsuarioId == u.UsuarioId), "LivroId", "Titulo");
return View(emprestimo);
}
//ViewBag.LivroId = new SelectList(db.Livro, "LivroId", "Titulo", emprestimo.LivroId);
//ViewBag.UsuarioId = new SelectList(db.Usuario, "UsuarioId", "Nome", emprestimo.UsuarioId);
//return View(emprestimo);
}
// POST: Emprestimo/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]
[AuthFilter]
public ActionResult Edit([Bind(Include = "EmprestimoId,Emprestado,PessoaEmprestimo,DataEmprestimo,DataDevolucao,LivroId,UsuarioId")] Emprestimo emprestimo)
{
if (ModelState.IsValid)
{
Usuario u = Session["usuario"] as Usuario;
emprestimo.UsuarioId = u.UsuarioId;
db.Entry(emprestimo).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.LivroId = new SelectList(db.Livro, "LivroId", "Titulo", emprestimo.LivroId);
ViewBag.UsuarioId = new SelectList(db.Usuario, "UsuarioId", "Nome", emprestimo.UsuarioId);
return View(emprestimo);
}
// GET: Emprestimo/Delete/5
[AuthFilter]
public ActionResult Delete(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Emprestimo emprestimo = db.Emprestimo.Find(id);
if (emprestimo == null)
{
return HttpNotFound();
}
return View(emprestimo);
}
// POST: Emprestimo/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
[AuthFilter]
public ActionResult DeleteConfirmed(int id)
{
Emprestimo emprestimo = db.Emprestimo.Find(id);
db.Emprestimo.Remove(emprestimo);
db.SaveChanges();
return RedirectToAction("Index");
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
}
}
Index.cshtml (Emprestimo)
#model IEnumerable<PersonalLibrary.Models.Emprestimo>
#{
ViewBag.Title = "Livros Emprestados";
}
<p>
#Html.ActionLink("Cadastrar Empréstimo", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Livro.Titulo)
</th>
<th>
#Html.DisplayNameFor(model => model.Emprestado)
</th>
<th>
#Html.DisplayNameFor(model => model.PessoaEmprestimo)
</th>
<th>
#Html.DisplayNameFor(model => model.DataEmprestimo)
</th>
<th>
#Html.DisplayNameFor(model => model.DataDevolucao)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.LivroId)
</td>
<td>
#Html.DisplayFor(modelItem => item.Emprestado)
</td>
<td>
#Html.DisplayFor(modelItem => item.PessoaEmprestimo)
</td>
<td>
#Html.DisplayFor(modelItem => item.DataEmprestimo)
</td>
<td>
#Html.DisplayFor(modelItem => item.DataDevolucao)
</td>
<td>
#Html.ActionLink("Editar", "Edit", new { id=item.EmprestimoId }) |
#Html.ActionLink("Detalhes", "Details", new { id=item.EmprestimoId }) |
#Html.ActionLink("Excluir", "Delete", new { id=item.EmprestimoId })
</td>
</tr>
}
</table>
That is because the Livro property is virtual, therefore it will not be loaded unless you access the property or load it like this:
List<Emprestimo> lista = ctx.Emprestimo.Include(x => x.Livro).Where(c => c.UsuarioId == id).ToList();
This is called eager loading. By default EF is configured for lazy loading.
On your Emprestimo.Edit controller action try loading the Livro entity:
Emprestimo emprestimo = db.Emprestimo.Find(id);
if (emprestimo == null)
{
return HttpNotFound();
}
db.Entry(emprestimo).Reference(e => e.Livro).Load();
On a side note, you also don't need to create new context to get the Livros list, you have a perfectly good one already (db).
So instead of:
using (LibraryContext ctx = new LibraryContext())
{
Usuario u = (Usuario)Session["usuario"];
ViewBag.LivroId = new SelectList(db.Livro.Where(c => c.UsuarioId == u.UsuarioId), "LivroId", "Titulo");
return View(emprestimo);
}
Just do:
Usuario u = (Usuario)Session["usuario"];
ViewBag.LivroId = new SelectList(db.Livro.Where(c => c.UsuarioId == u.UsuarioId), "LivroId", "Titulo");
return View(emprestimo);
And, you actually don't need this new context anywhere, just use the "db" one.

CheckBoxList Update Issue

I'm using ASP.NET Core MVC, EntityFramework Core and C#.
I'm having trouble updating the CheckBoxList values to the CustomerDevice table in my Database on the [HttpPost] Edit Action Method CustomerDeviceController. The CustomerDevice table is a JOIN table that has a Many to Many between the Customer and Device tables.
My Index Action Method for the CustomerDeviceController displays a list of Customers from my Customers table. I have an ActionLink labeled "Edit" that passes the CustId value to the CustomerDeviceController [HttpGet] Edit(int? id) Action Method which then displays all selected DevId values assigned to the CustId to the CheckBoxList, this part works fine.
When I try to update the CheckBoxList values now it adds the DevId values that I choose to remove and the DevId values that I want to keep it removes.
So for Example: If I initially added DevId values (1, 3, 4 and 7) for CustId 1006 and then decide to Edit to remove just DevId value 1 it actually removes DevId values (3, 4 and 7) and leaves DevId value 1.
Models
public class CheckBoxListItem
{
public int ID { get; set; }
public string Display { get; set; }
public bool IsChecked { get; set; }
}
public class Customer
{
public int CustId { get; set; }
public string CustDisplayName { get; set; }
public string CustFirstName { get; set; }
public string CustLastName { get; set; }
public List<CustomerDevice> CustomerDevices { get; set; }
}
public class CustomerDevice
{
public int CustId { get; set; }
public int DevId { get; set; }
public Customer Customer { get; set; }
public Device Device { get; set; }
}
public class Device
{
public int DevId { get; set; }
public string DevType { get; set; }
public List<CustomerDevice> CustomerDevices { get; set; }
}
public class WebFormContext : DbContext
{
public WebFormContext(DbContextOptions<WebFormContext> options)
: base(options)
{ }
public DbSet<Customer> Customers { get; set; }
public DbSet<Device> Devices { get; set; }
public DbSet<CustomerDevice> CustomerDevices { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>()
.HasKey(c => c.CustId);
modelBuilder.Entity<Customer>()
.Property(c => c.CustDisplayName)
.HasColumnType("varchar(100)")
.HasMaxLength(100)
.IsRequired();
modelBuilder.Entity<Customer>()
.Property(c => c.CustFirstName)
.HasColumnType("varchar(50)")
.HasMaxLength(50);
modelBuilder.Entity<Customer>()
.Property(c => c.CustLastName)
.HasColumnType("varchar(50)")
.HasMaxLength(50);
modelBuilder.Entity<Device>()
.HasKey(d => d.DevId);
modelBuilder.Entity<Device>()
.Property(d => d.DevType)
.HasColumnType("varchar(50)")
.HasMaxLength(50)
.IsRequired();
modelBuilder.Entity<CustomerDevice>()
.HasKey(c => new { c.CustId, c.DevId });
modelBuilder.Entity<CustomerDevice>()
.HasOne(cd => cd.Customer)
.WithMany(c => c.CustomerDevices)
.HasForeignKey(cd => cd.CustId);
modelBuilder.Entity<CustomerDevice>()
.HasOne(cd => cd.Device)
.WithMany(d => d.CustomerDevices)
.HasForeignKey(cd => cd.DevId);
}
}
ViewModel
public class CustomerDeviceFormViewModel
{
public int CustId { get; set; }
public string CustDisplayName { get; set; }
public List<CheckBoxListItem> Devices { get; set; }
}
Controller
public class CustomerDeviceController : Controller
{
// GET: /<controller>/
public IActionResult Index()
{
return View(db.Customers.ToList());
}
public ActionResult Edit(int? id)
{
Customer customer = db.Customers.SingleOrDefault(c => c.CustId == id);
if (customer == null)
{
return NotFound();
}
// Get all devices
var deviceList = db.Devices.ToList();
// Get the selected device ID's for the customer
IEnumerable<int> selectedDevices = db.CustomerDevices
.Where(x => x.CustId == id).Select(x => x.DevId);
// Build view model
var model = new CustomerDeviceFormViewModel()
{
CustId = customer.CustId,
CustDisplayName = customer.CustDisplayName,
Devices = deviceList.Select(x => new CheckBoxListItem()
{
ID = x.DevId,
Display = x.DevType,
IsChecked = selectedDevices.Contains(x.DevId)
}).ToList()
};
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(CustomerDeviceFormViewModel vmEdit)
{
if (ModelState.IsValid)
{
Customer customer = db.Customers
.Include(c => c.CustomerDevices)
.SingleOrDefault(c => c.CustId == vmEdit.CustId);
if (customer == null)
{
return NotFound();
}
IEnumerable<int> selectedDevices = vmEdit.Devices.Where(x => x.IsChecked).Select(x => x.ID);
// Add the new selected devices
foreach (int deviceId in selectedDevices)
{
var customerDevice = customer.CustomerDevices.FirstOrDefault(cd => cd.DevId == deviceId);
if (customerDevice != null)
{
customer.CustomerDevices.Remove(customerDevice);
}
else
{
CustomerDevice custDevice = new CustomerDevice
{
CustId = customer.CustId,
DevId = deviceId
};
customer.CustomerDevices.Add(custDevice);
}
}
// Update the customer
db.Customers.Update(customer); //or just db.Update(customer); same thing
// Save and redirect
db.SaveChanges();
return RedirectToAction("Index");
}
return View(vmEdit);
}
}
Shared/EditorTemplates/CheckBoxListItem.chtml
<div class="checkbox">
<label>
#Html.HiddenFor(x => x.ID)
#Html.CheckBoxFor(x => x.IsChecked)
#Html.LabelFor(x => x.IsChecked, Model.Display)
</label>
<br />
Edit View
<div class="form-group">
Please select the Devices to assign to <b>#Html.DisplayFor(c => c.CustDisplayName)</b>
</div>
<div class="form-group">
#Html.EditorFor(x => x.Devices)
</div>
#Html.HiddenFor(c => c.CustId)
<div class="form-group">
<button type="submit" class="btn btn-primary">Submit</button>
</div>

ASP.NET MVC Edit CheckBoxList values in Database

I'm having trouble understanding how to retrieve and edit the DevId values from my CustomerDevice table in my database to the CheckBoxList based on the CustId value.
My Index Action Method for the CustomerDeviceController displays a list of Customers from my Customers table. I have an ActionLink labeled "Edit" that passes the CustId value to the CustomerDeviceController [HttpGet] Edit(int? id) Action Method which currently displays all CheckBoxListItem values from the Devices table. However, the CheckBoxList does not display the checked DevId values from the CustomerDevice table in the database to the CheckBoxList that pertain to the CustId, instead it displays a check for each of the CheckBoxList values.
The part that I'm having trouble understanding and figuring out, is how can I display the selected DevId values from the CustomerDevice table in my database to the CheckBoxList based on the CustId and then Edit/Update the modified CheckBoxListItems on the [HttpPost] Edit Action Method back to my CustomerDevice table in my database if need be.
Please see the following code below that I have so far.
Models
public class CheckBoxListItem
{
public int ID { get; set; }
public string Display { get; set; }
public bool IsChecked { get; set; }
}
public class Customer
{
public int CustId { get; set; }
public string CustDisplayName { get; set; }
public string CustFirstName { get; set; }
....
}
public class Device
{
public int DevId { get; set; }
public string DevType { get; set; }
}
public class CustomerDevice
{
public int CustId { get; set; }
public int DevId { get; set; }
public Customer Customer { get; set; }
public Device Device { get; set; }
}
ViewModels
public class CustomerDeviceFormViewModel
{
public int CustId { get; set; }
public string CustDisplayName { get; set; }
public List<CheckBoxListItem> Devices { get; set; }
}
CustomerDeviceController
public ActionResult Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var customervm = new CustomerDeviceFormViewModel();
Customer customer = db.Customers.SingleOrDefault(c => c.CustId == id);
if (customer == null)
{
return NotFound();
}
customervm.CustId = customer.CustId;
customervm.CustDisplayName = customer.CustDisplayName;
// Retrieves list of Devices for CheckBoxList
var deviceList = db.Devices.ToList();
var checkBoxListItems = new List<CheckBoxListItem>();
foreach (var device in deviceList)
{
checkBoxListItems.Add(new CheckBoxListItem()
{
ID = device.DevId,
Display = device.DevType,
IsChecked = deviceList.Where(x => x.DevId == device.DevId).Any()
});
}
customervm.Devices = checkBoxListItems;
return View(customervm);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(CustomerDeviceFormViewModel vmEdit)
{
if (ModelState.IsValid)
{
Customer customer = db.Customers.SingleOrDefault(c => c.CustId == vmEdit.CustId);
if (customer == null)
{
return NotFound();
}
foreach (var deviceId in vmEdit.Devices.Where(x => x.IsChecked).Select(x => x.ID))
{
var customerDevices = new CustomerDevice
{
CustId = vmEdit.CustId,
DevId = deviceId
};
db.Entry(customerDevices).State = EntityState.Modified;
}
db.SaveChanges();
return RedirectToAction("Index");
}
return View(vmEdit);
}
Edit.chtml
<div class="form-group">
Please select the Devices to assign to <b>#Html.DisplayFor(c => c.CustDisplayName)</b>
</div>
<div class="form-group">
#Html.EditorFor(x => x.Devices)
</div>
#Html.HiddenFor(c => c.CustId)
<div class="form-group">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
Shared/EditorTemplate/CheckBoxListItem.chtml
<div class="checkbox">
<label>
#Html.HiddenFor(x => x.ID)
#Html.CheckBoxFor(x => x.IsChecked)
#Html.LabelFor(x => x.IsChecked, Model.Display)
</label>
<br />
Your code for setting the IsChecked value will always return true (your loop is basically say if the collection contains me (which of course it does) then set it to true).
You need to get the selected values for each Customer by reading the values from your CustomerDevice table
Customer customer = db.Customers.SingleOrDefault(c => c.CustId == id);
if (customer == null)
{
return NotFound();
}
// Get all devices
var deviceList = db.Devices.ToList();
// Get the selected device ID's for the customer
IEnumerable<int> selectedDevices = db.CustomerDevices
.Where(x => x.CustId == id).Select(x => x.DevId);
// Build view model
var model = new CustomerDeviceFormViewModel()
{
CustId = customer.CustId,
CustDisplayName = customer.CustDisplayName,
Devices = deviceList.Select(x => new CheckBoxListItem()
{
ID = x.DevId,
Display = x.DevType,
IsChecked = selectedDevices.Contains(x.DevId)
}).ToList()
};
return View(model);
Here's a snippet of Razor code that I've used:
foreach (SelectListItem p in Model.PositionList)
{
#Html.Raw(p.Text + "<input type=checkbox name=\"PositionIDs\" id=\"PositionIDs\" value=" + #p.Value + (Model.Positions != null && Model.Positions.Any(pos => pos.ScoreCardId == Convert.ToInt32(p.Value)) ? " checked />" : " />"));
}
You might want to have a look at the MvcCheckBoxList NuGet package:
https://www.nuget.org/packages/MvcCheckBoxList/
This makes doing some powerful stuff with a CheckBoxList much easier in MVC - and may be a better approach to fixing your CheckBox issues.

Categories

Resources