ASP.NET MVC Insert CheckBoxList values to Database - c#

I'm having trouble understanding how to add my CustId and selected DevId values from the CheckBoxList to my CustomerDevice table in my database.
My Index Action Method for the CustomerDeviceController displays a list of Customers from my Customers table. I have a link labeled "Add Device(s)" that passes the CustId value to the CustomerDeviceController [HttpGet] Create Action Method which displays my CheckBoxListItem values from the Devices table which works fine.
The part that I'm having trouble understanding and figuring out, is how can I add the selected DevId values from the CheckBoxList along with the CustId value to my CustomerDevice Table on the [HttpPost] Create Action Method.
Please see the following code below that I have so far.
CheckBoxListItem Model
public class CheckBoxListItem
{
public int ID { get; set; }
public string Display { get; set; }
public bool IsChecked { get; set; }
}
Customer Model
public class Customer
{
public int CustId { get; set; }
public string CustDisplayName { get; set; }
public string CustFirstName { get; set; }
public string CustLastName { get; set; }
public string CustCompanyName { get; set; }
public string CustAddress { get; set; }
public string CustPhoneNumber { get; set; }
public string CustMobileNumber { get; set; }
public string CustEmailAddress { get; set; }
}
Device Model
public class Device
{
public int DevId { get; set; }
public string DevType { get; set; }
}
CustomerDevice Model
public class CustomerDevice
{
public int CustId { get; set; }
public int DevId { get; set; }
public Customer Customer { get; set; }
public Device Device { get; set; }
}
Shared/EditorTemplates/CheckBoxListItem.cshtml
<div class="checkbox">
<label>
#Html.HiddenFor(x => x.ID)
#Html.CheckBoxFor(x => x.IsChecked)
#Html.LabelFor(x => x.IsChecked, Model.Display)
</label>
<br />
CustomerDeviceFormViewModel
public class CustomerDeviceFormViewModel
{
public int CustId { get; set; }
public string CustDisplayName { get; set; }
public List<CheckBoxListItem> Devices { get; set; }
}
CustomerDeviceController
public class CustomerDeviceController : Controller
{
private CheckBoxAppContext db;
public CustomerDeviceController(CheckBoxAppContext context)
{
db = context;
}
// GET: /<controller>/
public IActionResult Index()
{
return View(db.Customers.ToList());
}
public ActionResult Create(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;
// 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 = false //On the create view, no devices are selected by default
});
}
customervm.Devices = checkBoxListItems;
return View(customervm);
}
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(CustomerDeviceFormViewModel vm)
{
if (ModelState.IsValid)
{
var customerDevices = new CustomerDevice();
{
customerDevices.CustId = vm.CustId;
var deviceList = db.Devices.ToList();
var checkBoxListItems = new List<CheckBoxListItem>();
foreach (var deviceId in deviceList)
{
}
}
db.CustomerDevices.Add(customerDevices);
db.SaveChanges();
return RedirectToAction("Index");
}
else
{
return View(vm);
}
}
}
Index View
<table class="table">
<tr>
<th>Id</th>
<th>Display Name</th>
<th>Actions</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.CustId)
</td>
<td>
#Html.DisplayFor(modelItem => item.CustDisplayName)
</td>
<td>
#Html.ActionLink("Add Device(s)", "Create", new { id = item.CustId })
</td>
</tr>
}
Create View
<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>

Assuming that your CustomerDevice is the join table for a M:N relation between Customer and Device entities, I think you need something like this in your POST action:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(CustomerDeviceFormViewModel vm)
{
if (ModelState.IsValid)
{
foreach (var deviceId in vm.Devices.Where(x => x.IsChecked).Select(x => x.ID))
{
var customerDevices = new CustomerDevice
{
CustId = vm.CustId,
DevId = deviceId
};
db.CustomerDevices.Add(customerDevices);
}
db.SaveChanges();
return RedirectToAction("Index");
}
return View(vm);
}
You create an entity of the join table and add it to the context. EF will associate the records when you call SaveChanges.
Hope this helps!

Related

Configure many-to-many relationship in ASP.NET Core MVC and Entity Framework against

I am trying to configure the relationship many-to-many between two tables, Employee and Project.
One Employee can participate in many projects, and one project can have many Employees working on it. So I created two model classes Employee and Project, and I added the table Employee_Project.
These are my three model classes:
namespace WebApp2.Models
{
public class Employee
{
[Key]
public int Emp_Id { get; set; }
public string Emp_Name { get; set; }
public string Emp_Email { get; set; }
public string Emp_Mobile { get; set; }
public virtual ICollection<Employee_Project> Employee_Projects { get; set; }
}
public class Project
{
[Key]
public int Proj_Id { get; set; }
public string Proj_Name { get; set; }
public string Project_Details { get; set; }
public virtual ICollection<Employee_Project> Employee_Projects { get; set; }
}
public class Employee_Project
{
[Key]
[Column(Order =1)]
public int Emp_Id { get; set; }
[Key]
[Column(Order = 2)]
public int Proj_Id { get; set; }
public virtual Employee Employee { get; set; }
public virtual Project Project { get; set; }
}
}
I then added this DbContext class:
namespace WebApp2.Data
{
public class MyDbContext:DbContext
{
public MyDbContext(DbContextOptions<MyDbContext> option):base(option)
{
}
public DbSet<Employee> Employees { get; set; }
public DbSet<Project> Projects { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Employee_Project>().HasKey(pt => new { pt.Proj_Id, pt.Emp_Id });
modelBuilder.Entity<Employee_Project>()
.HasOne(pt => pt.Employee)
.WithMany(pt => pt.Employee_Projects)
.HasForeignKey(p => p.Emp_Id);
modelBuilder.Entity<Employee_Project>()
.HasOne(pt => pt.Project)
.WithMany(pt => pt.Employee_Projects)
.HasForeignKey(p => p.Proj_Id);
}
public DbSet<Employee_Project> Employee_Projects { get; set; }
}
}
I created after that the three controllers
public class ProjectController : Controller
{
private readonly MyDbContext _context;
public ProjectController(MyDbContext context)
{
_context = context;
}
public IActionResult Index()
{
return View(_context.projects.ToList());
}
public IActionResult Create()
{
return View();
}
[HttpPost]
public IActionResult Create(Project project)
{
_context.projects.Add(project);
_context.SaveChanges();
return RedirectToAction("Index");
}
}
public class EmployeeController : Controller
{
private readonly MyDbContext _context;
public EmployeeController(MyDbContext context)
{
_context = context;
}
public IActionResult Index()
{
return View(_context.Employees.ToList());
}
public IActionResult Create()
{
return View();
}
[HttpPost]
public IActionResult Create(Employee employee)
{
_context.Employees.Add(employee);
_context.SaveChanges();
return RedirectToAction("Index");
}
}
public class Emp_ProjController : Controller
{
private readonly MyDbContext _DbContext;
public Emp_ProjController(MyDbContext DbContext)
{
_DbContext = DbContext;
}
public IActionResult Index()
{
return View(_DbContext.Employee_Projects.ToList());
}
public IActionResult Create()
{
ViewBag.emp=_DbContext.Employees.ToList();
ViewBag.pro=_DbContext.projects.ToList();
return View();
}
[HttpPost]
public IActionResult Create(int empid, int [] projIds)
{
foreach (var item in projIds)
{
Employee_Project emp = new Employee_Project();
emp.Emp_Id = empid;
emp.Proj_Id = item;
_DbContext.Employee_Projects.Add(emp);
_DbContext.SaveChanges();
}
return RedirectToAction("Index");
}
}
After that foreach Controllers I made the view for the method Index and Create
Emp_Proj
//view Index
#model IEnumerable<WebApp2.Models.Employee_Project>
#{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.Employee.Emp_Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Project.Proj_Name)
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Employee.Emp_Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Project.Proj_Name)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |
#Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
</td>
</tr>
}
</tbody>
</table>
//view Create
<h2>Create</h2>
<form method="post">
<div>
<label>Employee Name</label>
#Html.DropDownList("empid", new SelectList(ViewBag.emp, "Emp_Id","Emp_Email"),"Select Employee")
</div>
<div>
<label>Select Project</label>
#* #Html.DropDownList("proid", new SelectList(ViewBag.pro, "Proj_Id","Proj_Name"),"Select Project")*#
<ul>
#foreach(var item in ViewBag.pro )
{
<li>
<input type="checkbox" name="projIds" value="#item.Proj_Id">#item.Proj_Name
</li>
}
</ul>
<input type="submit" value="SaveData"/>
</div>
</form>
I don't have problem in the Employee and the project, I found the problem when I want to create a Emp_Proj element
enter image description here
It always gives me an error like that:
SqlException: Violation of PRIMARY KEY constraint 'PK_Employee_Projects'. Cannot insert duplicate key into object 'dbo.Employee_Projects'. Duplicate key value: (1, 1).
The instruction has been terminated.
enter image description here
Can someone please help me to find the problem? Thanks in advance.
I try to find the problem, and I appreciate some assistance.
The error message has show the reason why cause this exception: The database already contains this record, you can't insert duplicate data. You just need to check if it already exists in the database before inserting data, Please refer to this simple demo.
[HttpPost]
public IActionResult Create(int empid, int[] projIds)
{
foreach (var item in projIds)
{
//check if the database already has this record
var empdb = _DbContext.Employee_Projects.Where(x => x.Emp_Id == empid && x.Proj_Id == item).FirstOrDefault();
if (empdb==null)
{
Employee_Project emp = new Employee_Project();
emp.Emp_Id = empid;
emp.Proj_Id = item;
_DbContext.Employee_Projects.Add(emp);
}
}
_DbContext.SaveChanges();
return RedirectToAction("Index");
}
First off, try to remove "Employee_Project" entity entirely, and see if EF Core can auto-create it. EF Core should be able to auto-generate the association table as long as the two navigational properties are set on the two Entities. Note that navigational properties of the two entities are supposed to reference each other. like so;
in Employee;
public virtual ICollection<Project> Projects { get; set; }
and in Project;
public virtual ICollection<Employee> Employees { get; set; }
In case that doesn't work, modify your Employee_Project class like this;
public class Employee_Project
{
[Key]
[Column(Order =1)]
public int EmployeeId { get; set; }
[Key]
[Column(Order = 2)]
public int ProjectId { get; set; }
public virtual Employee Employee { get; set; }
public virtual Project Project { get; set; }
}
The naming of the columns might have been the problem.

Saving to the database when the button in the view is clicked

There are 2 models. The user is logging into the system. I want a value from the current model to be added to the logged in user's table when he clicks the button in the Forum View. Ogrenci Model enters the system. When the button is clicked, I want ProjectName to be added to the BekleyenProje column in the Ogrenci Model. How can I do that?
Model 1:
public class Ogrenci
{
public int OgrenciID { get; set; }
public int OgrenciNumarasi { get; set; }
public string Ad { get; set; }
public string Soyad { get; set; }
public string Bolum { get; set; }
public short Sinif { get; set; }
public string Yetenekler { get; set; }
public string Sifre { get; set; }
public string BekleyenProje { get; set; }
public string OnaylananProje { get; set; }
//FK
public List<Proje> Projeler { get; set; }
}
Model 2:
public class Proje
{
public int ProjeID { get; set; }
public string ProjeAdi { get; set; }
public string Aciklama { get; set; }
public DateTime EklenmeTarihi { get; set; }
//FK
public int OgrenciID { get; set; }
public Ogrenci Ogrenci { get; set; }
}
ForumController:
public class ForumController : Controller
{
private OgrenciContext db = new OgrenciContext();
// GET: Forum
public ActionResult Index()
{
//Include(o => o.Ogrenci) -- öğrenci bilgilerini dahil ediyoruz
return View(db.Projeler.Include(o => o.Ogrenci).ToList());
}
}
Forum Index View (The button I'm talking about is here):
#model IEnumerable<DonemProjesi.Models.Proje>
#{
ViewBag.Title = "Index";
}
<table class="table table-striped table-bordered table-hover table-condensed cols-3 custom_table">
<thead>
<tr>
<th scope="col">Proje</th>
<th scope="col">Etkileşimler</th>
<th scope="col">Yayınlanma Tarihi</th>
<th scope="col">Detay</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
<div>#Html.DisplayFor(modelItem => item.ProjeAdi)</div>
<small>#Html.DisplayFor(modelItem => item.Ogrenci.Ad)</small>
</td>
<td>
<ul class="activity_outer">
<li><strong>03</strong><span>Başvuranlar</span></li>
<li><strong>01</strong><span>Dahil olanlar</span></li>
</ul>
</td>
<td>
<div class="last_activity"><span class="time_ago">#Html.DisplayFor(modelItem => item.EklenmeTarihi)</span></div>
</td>
<td>
<button type="button" class="login-button">#Html.ActionLink("Proje Detayı", "Details", "Proje", new { id = item.ProjeID }, new { #class = "detayy" })</button>
<button type="button" class="login-button"></button> //BUTTON IS HERE
</td>
</tr>
}
</tbody>
Also, Controller for Login:
public class SecurityController : Controller
{
OgrenciContext db = new OgrenciContext();
// GET: Security
public ActionResult Login()
{
return View();
}
[HttpPost]
public ActionResult Login(Ogrenci ogrenci)
{
var kullanici = db.Ogrenciler.FirstOrDefault(x=>x.OgrenciNumarasi == ogrenci.OgrenciNumarasi && x.Sifre == ogrenci.Sifre);
if (kullanici!=null)
{
FormsAuthentication.SetAuthCookie(kullanici.Ad, false);
Session.Add("OgrenciID", kullanici.OgrenciID); //kimlik doğrulamasu yapılan kullanıcının ID'si alınıyor
return RedirectToAction("Details","Ogrenci", new {#id=kullanici.OgrenciID });
}
else
{
ViewBag.Mesaj = "Geçersiz numara veya şifre girdiniz!";
return View();
}
}
}
It's better to use repository pattern, but a direct solution would be:
kullanici.BekleyenProje = Request["ProjectName"];
db.SaveChanges();
Also it depends on how many properties you want to pass. If it's only one, you can send it in the Request. Otherwise, you create a view model with the necessary members.
Make sure the button is submitting the form and ProjectName is a hidden field inside the form.

How to show dynamic category in razor page?

i have create dynamic category with subcategory ...
i can't show on my razor page i used foreach but foreach for 2 step category or 3 step my category have unlimited step
plz help me
public class vmCategoryForSearch
{
public int CategoryId { get; set; }
public string titleEn { get; set; }
public bool isDelete { get; set; }
public List<vmCategoryForSearch> Children { get; set; }
}
how to call this code
public List<vmCategoryForSearch> GetAllCategoryForSearchClients()
{
return catsearchList.Where(mm => mm.ParentId == 0).Select(m => new vmCategoryForSearch
{
titleEn = m.titleEn,
titleFa = m.titleFa,
Children = GetAllCategoryForSearchClients(m.SubId)
}).ToList();
}
private List<vmCategoryForSearch> GetAllCategoryForSearchClients(int id)
{
return catsearchList.Where(sm => sm.ParentId == id).Select(vm => new vmCategoryForSearch
{
titleEn = vm.titleEn,
titleFa = vm.titleFa,
Children = GetAllCategoryForSearchClients(vm.SubId)
}).ToList();
}
my razor page
Here is a simple worked example , you could refer to :
Category model
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public int ParentId { get; set; }
}
vmCategoryForSearch
public class vmCategoryForSearch
{
public int CategoryId { get; set; }
public string Name { get; set; }
public List<vmCategoryForSearch> Children { get; set; }
}
PageModel
private readonly RazorPages2_1Project.Data.RazorPagesDbContext _context;
private readonly List<Category> list;
public CreateModel(RazorPages2_1Project.Data.RazorPagesDbContext context)
{
_context = context;
list = _context.Category.ToList();
}
public IActionResult OnGet()
{
CategoryList = GetAllCategoryForSearchClients();
return Page();
}
[BindProperty]
public List<vmCategoryForSearch> CategoryList { get; set; }
public List<vmCategoryForSearch> GetAllCategoryForSearchClients()
{
var result= list.Where(mm => mm.ParentId == 0).Select(m => new vmCategoryForSearch
{
Name = m.Name,
Children = GetAllCategoryForSearchClients(m.Id)
}).ToList();
return result;
}
private List<vmCategoryForSearch> GetAllCategoryForSearchClients(int id)
{
var result1 = list.Where(sm => sm.ParentId == id).Select(vm => new vmCategoryForSearch
{
Name = vm.Name,
Children = GetAllCategoryForSearchClients(vm.Id)
}).ToList();
return result1;
}
}
Razor Pages
<div class="c-box_item">
#foreach(var item in Model.CategoryList)
{
<ul >
<li>
<span> #item.Name</span>
<div>
#foreach(var subitem in item.Children)
{
<ul>
<li>
<span> #subitem.Name</span>
<div>
#if (subitem.Children.Count > 0)
{
#foreach (var subitems in subitem.Children)
{}
}
</div>
</li>
</ul>
}
</div>
</li>
</ul>
}
</div>

HTML BeginCollectionItem returns NULL

HTML.BeginCollectionItem does not return values to the controller. It always return NULL in the controller. I am not sure if has got anything to do if there is a partial view within another partial view. Below is the snippet of the code/view.
ProductEditModel
public class ProductEditModel
{
// Product details displayed on edit form
public Product ProductModel { get; set; }
public IList<ProductAssetAudioEditModel> ProductAssetAudios { get; set;}
}
ProductAssetAudioEditModel
public class ProductAssetAudioEditModel
{
public int ProductId { get; set; }
public int? ProductAssetId { get; set; }
public virtual IList<ProductAssetResourceEditModel> ProductAssetResources { get; set; }
}
ProductAssetResourceEditModel
public class ProductAssetResourceEditModel
{
public int? ProductAssetResourceId { get; set; }
public int ProductAssetId { get; set; }
public int ResourceNumber { get; set; }
public int? ElectronicFileId { get; set; }
public ElectronicFile ElectronicFile { get; set; }
}
ProductEditView.cshtml
<div id="audio">
#foreach (ProductAssetAudioEditModel audio in Model.ProductAssetAudios)
{
Html.RenderPartial("_ProductAssetAudioRow", audio);
}
</div>
_ProductAssetAudioRow.cshtml
#using (Html.BeginCollectionItem("ProductAssetAudios"))
{
....
<tbody>
#foreach (var resource in Model.ProductAssetResources)
{
Html.RenderPartial("_ProductAssetAudioResource", resource);
}
</tbody>
.....
}
_ProductAssetAudioResource
#using (Html.BeginCollectionItem("ProductAssetResources"))
{
#Html.HiddenFor(m => Model.ProductAssetResourceId)
#Html.HiddenFor(m => Model.ProductAssetId)
<td>
#if (Model.ElectronicFileId.HasValue)
{
#Html.HiddenFor(model => model.ElectronicFileId)
#Html.ActionLink(Model.ElectronicFile.FileName, "Details", "File", new { id = Model.ElectronicFileId, area = "Edi" }, null);
}
</td>
<td>
#Html.EditorFor(c => Model.TrackTitle)
</td>
}
In the controller , ProductAssetResources is NULL even though edit page binds the properties correctly for editing.
I am not sure what I am missing here.
-Alan-

#Html.DropDownList or #Html.DropdownlistFor doesn't show current value

So I have an Html.DropDownList that I want to use on an edit page to allow users to edit the categories of different recipe ingredients. So for example if a user wants to edit the chili recipe they are shown all the different properties for the recipe including a list of all the ingredients which they should be able to add to or delete from or edit the quantity, name and category of. And I only want to have a drop down list to edit the category because I only want them to be able to choose from a preselected list of categories. Here is what I have.
<table>
<tr>
<th></th>
</tr>
#for (int i = 0; i < #Model.Recipe.RecipeIngredients.Count; i++)
{
<tr>
<td>#Html.EditorFor(model => model.Recipe.RecipeIngredients[i].Quantity)</td>
<td>#Html.EditorFor(model => model.Recipe.RecipeIngredients[i].IngredientName)</td>
<td>#Html.DropDownList("AvailableCategories")</td>
</tr>
}
</table>
//// GET: /Recipe/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
EditViewModel vm = new EditViewModel();
vm.Recipe = Db.Recipes.Find(id);
if (vm.Recipe == null)
{
return HttpNotFound();
}
List<SelectListItem> items = new List<SelectListItem>();
foreach (var item in Db.Categories)
{
items.Add(new SelectListItem { Text = item.CategoryName, Value = item.CategoryId.ToString() });
}
vm.AvailableCategories = items;
return View(vm);
}
public class EditViewModel
{
public Recipe Recipe { get; set; }
public List<SelectListItem> AvailableCategories { get; set; }
public EditViewModel()
{
AvailableCategories = new List<SelectListItem>();
}
}
public class RecipeIngredient
{
public int IngredientId { get; set; }
public int RecipeId { get; set; }
public int CategoryId { get; set; }
public string IngredientName { get; set; }
public string Quantity { get; set; }
public int IsOnMenu { get; set; }
public bool IsOnTheDamnMenu
{
get
{
return IsOnMenu == 1;
}
set
{
IsOnMenu = value ? 1 : 0;
}
}
public virtual Recipe Recipe { get; set; }
public virtual Category Category { get; set; }
}
I actually just found that I can pass more properties into this. So something like
#Html.DropDownListFor(model => model.Recipe.RecipeIngredients[i].CategoryId, new SelectList(Model.AvailableCategories, "Value", "Text", Model.Recipe.RecipeIngredients[i].CategoryId), "-Select-")
Shows the correct category now.

Categories

Resources