Self-referencing model MVC 4 - asp.net-mvc

I have a component that have a list of subcomponents:
public class Componente
{
public virtual int ComponentId { get; set; }
public virtual string NameComp { get; set; }
public virtual List<Subcomponent> Subcomponents { get; set; }
}
public class Subcomponent
{
public virtual int SubcomponentId { get; set; }
public virtual int ParentId { get; set; }
public virtual string NameSub { get; set; }
public virtual Component CompSub { get; set; }
public virtual List<Subcomponent> Children { get; set; }
}
Then i have a view that shows the components, their subcomponents and in the future the subsubcomponents and so on.
<table>
...
if (item.Subcomponents.Count != 0)
{
foreach (var x in item.Subcomponents)
{
<tr>
<td>
#Html.DisplayFor(modelItem => x.NameSub)
</td>
<td>
#Html.ActionLink("Create Subcomponent", "CreateMoreSubcomponents", "Subcomponent", new { id = x.SubcomponentId }, null)
</td>
</tr>
}
}
}
</table>
And the create method:
public ActionResult CreateMoreSubcomponents(int id)
{
var x = db.Subcomponents.FirstOrDefault(e => e.SubcomponentId == id);
if (x == null)
{
return HttpNotFound();
}
var subcomponent = new Subcomponent()
{
ParentId = x.SubcomponentId
};
return View("CreateMoreSubcomponents", subcomponent);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateMoreSubcomponents(Subcomponent subcomponent)
{
if (ModelState.IsValid)
{
db.Subcomponents.Add(subcomponent);
db.SaveChanges();
...
}
The goal is to have subsubcomponents, subsubsubcomponents and so on. But the code that i show here is not working, it gives an error in the db.SaveChanges(); because it asks for the componentId but i don't know what do i have to change. What do i need to change or do? Thanks

Related

ASP.NET MVC : saving related entities on form submit

I require a little bit of help with ASP.NET MVC data entry. Can anybody help me how would I save a new product that is automatically tied to a catalog? It is a requirement that the product can not be created without a catalog. But I can save the product only without the catalog, and this is what I got so far:
Entities:
The Model:
public partial class Product
{
public Product()
{
this.ProductsInCatalog = new HashSet<ProductsInCatalog>();
}
public int Id { get; set; }
public string Code { get; set; }
public string Description { get; set; }
public virtual ICollection<ProductsInCatalog> ProductsInCatalog { get; set; }
}
public partial class Catalog
{
public Catalog()
{
this.ProductsInCatalog = new HashSet<ProductsInCatalog>();
}
public int Id { get; set; }
public string Code { get; set; }
public string Description { get; set; }
public virtual ICollection<ProductsInCatalog> ProductsInCatalog { get; set; }
}
public partial class ProductsInCatalog
{
public int ProductId { get; set; }
public int CatalogId { get; set; }
public virtual Product Product { get; set; }
public virtual Catalog Catalog { get; set; }
}
The Controller:
public ActionResult Create()
{
CatalogManager catalogManager = new CatalogManager();
ViewBag.Catalogs = catalogManager.GetCatalogs();
return View();
}
[HttpPost]
[ActionName("Save")]
public ActionResult Save(EURIS.Entities.Product product)
{
if (ModelState.IsValid)
{
ProductManager productManager = new ProductManager();
productManager.SaveProduct(product);
}
return RedirectToAction("Index");
}
The EFCore Manager:
public class ProductManager
{
readonly LocalDbEntities _context = new LocalDbEntities();
public List<Product> GetProducts()
{
var products = (from item in _context.Product select item).ToList();
return products;
}
public Product GetProductByID(int productID)
{
return _context.Product.SingleOrDefault(e => e.Id == productID);
}
public bool SaveProduct(Product product)
{
try
{
_context.Product.Add(product);
_context.SaveChanges();
return true;
}
catch (System.Exception exe)
{
return false;
}
}
public bool DeleteProduct(int productID)
{
try
{
Product product = _context.Product.SingleOrDefault(e => e.Id == productID);
_context.Product.Remove(product);
_context.SaveChanges();
return true;
}
catch (System.Exception exe)
{
return false;
}
}
public bool UpdateProduct(Product product)
{
try
{
Product prodToUpdate = _context.Product.Where(p => p.Id == product.Id).FirstOrDefault();
if (prodToUpdate != null)
{
_context.Entry(prodToUpdate).CurrentValues.SetValues(product);
_context.SaveChanges();
return true;
}
return false;
}
catch (System.Exception exe)
{
return false;
}
}
}
The View:
#model EURIS.Entities.Product
#{
ViewBag.Title = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>#ViewBag.Title</h2>
<p>
#Html.ActionLink("Back", "Index", "Product")
</p>
<table>
#using (Html.BeginForm("Save", "Product", FormMethod.Post))
{
<tr>
<td>Id</td>
<td>#Html.TextBoxFor(model => model.Id)</td>
<td>#Html.ValidationMessageFor(model => model.Id)</td>
</tr>
<tr>
<td>Code</td>
<td>#Html.TextBoxFor(model => model.Code)</td>
<td>#Html.ValidationMessageFor(model => model.Code)</td>
</tr>
<tr>
<td>Description</td>
<td>#Html.TextBoxFor(model => model.Description)</td>
<td>#Html.ValidationMessageFor(model => model.Description)</td>
</tr>
<tr>
<td>Catalog asoiation:</td>
<td>
<select id="ddlCatalog" onchange="onCatDdlChange">
#foreach (EURIS.Entities.Catalog cat in ViewBag.Catalogs)
{
<option value="#cat.Id">#cat.Code</option>
}
</select>
</td>
</tr>
<tr>
<td><button type="submit">Save</button></td>
</tr>
}
</table>

ASP.NET MVC ( Object reference not set to an instance of an object )

I have the following problem when displaying list of personal file,
but I used the (using System.Data.Entity) and (Include) in the query it's not working
Class Model
public class PersonalFile
{
public int Id { get; set; }
[StringLength(50)]
public string FileName { get; set; }
[StringLength(500)]
public string FilePath { get; set; }
public DateTime UploadDateTime { get; set; }
public int EmployeeId { get; set; }
[ForeignKey("EmployeeId")]
public Employee Employee { get; set; }
public int? FileCategoryId { get; set; }
[ForeignKey("FileCategoryId")]
public virtual FileCategory FileCategory { get; set; }
}
Class View Model
public class PersonalFileViewModel
{
public int Id { get; set; }
[StringLength(50)]
public string FileName { get; set; }
[StringLength(500)]
public string FilePath { get; set; }
public int EmployeeId { get; set; }
[Required]
public int FileCategoryId { get; set; }
public IEnumerable<PersonalFile> PersonalFiles { get; set; }
}
Class Model File Category
public class FileCategory
{
public int FileCategoryId { get; set; }
public string categoryName { get; set; }
public string optionGroup { get; set; }
public virtual ICollection<PersonalFile> PersonalFile { get; set; }
}
Action Method
public ActionResult Index(int? id)
{
if (id == null)
return HttpNotFound();
var empl = _dbContext.Employees.SingleOrDefault(c => c.EmployeeId == id);
if (empl == null)
return HttpNotFound();
ViewBag.FileCategoryId = new SelectList(_dbContext.FileCategory, "FileCategoryId", "categoryName" , "optionGroup", 0);
var vwModel = new PersonalFileViewModel
{
EmployeeId = empl.EmployeeId,
PersonalFiles = _dbContext.PersonalFiles.Include(p => p.Employee).Include(p => p.FileCategory).Where(x => x.EmployeeId == id)
};
NameAndImage(empl.EmployeeId);
return View(vwModel);
}
View
foreach (var j in Model.PersonalFiles)
{
<tr style="border-top: 1px solid black;">
<td style="vertical-align: middle;">#j.FileName</td>
<td style="vertical-align: middle;"> #j.FileCategory.categoryName </td>
<td style="vertical-align: middle;">#j.UploadDateTime</td>
<td>
Download
#Html.ActionLink("Delete", "Delete", new { j.Id }, new { #class = "btn btn-danger btn-sm", onclick = "return confirm('Do you really want to delete this file?');" })
</td>
</tr>
}
enter image description here
Firstly, make sure that with each PersonalFile belongs to FileCategory accordingly.
Secondly, You should .ToList() to force our query to execute immediately instead.
PersonalFiles = _dbContext.PersonalFiles.Include(p => p.Employee).Include(p => p.FileCategory).Where(x => x.EmployeeId == id).ToList()
I solved the problem by putting this line Code on the View
#(j.FileCategory == null ? "" : j.FileCategory.categoryName)

How to Insert checkbox checked values id in Many to Many relationship table?

I wanted to add the value of checkbox ids to many to many relation table between two tables Course-Student
i have 3 tables Course{ CourseID,CourseName }, Studdent{StudentID,StudentName} and CourseStudet{CourseID, StudentID}
Course table already have course{1.DataScience,2.English,3.Physics}
while Inserting Student i have shown course checkboxes with these options fetching list of these courses from database to show in view to user when he is about to register now i am confused how to insert into database?
public class CourseModel {
public List<Course> mycourse{ get; set; }
public string StudentName {get; set;}
}
EF generated Class
public partial class Course
{
public Course()
{
this.Student= new HashSet<Student>();
}
public int CourseID { get; set; }
public string CourseName { get; set; }
i have inserted this field to check for checked value
public bool Ischecked { get; set; }
public virtual ICollection<Student> Student { get; set; }
}
public partial class Student
{
public Student()
{
this.Course= new HashSet<Course>();
}
public int StudentID { get; set; }
public string StudentName { get; set; }
public virtual ICollection<Course> Course{ get; set; }
}
public ActionResult Index()
{
CourseModel coursemodel = new CourseModel();
using (Entities db = new Entities())
{
coursemodel.mycourse = db.Course.ToList<Course>();
return View(coursemodel);
}
}
[HttpPost]
public ActionResult Index(CourseModel course)
{
return View(course);
}
View
#using (Html.BeginForm("index", "Course", FormMethod.Post))
<input type="Text" name="StudentName" placeholder="Name" />
{
<table>
#for (int i = 0 ; i < Model.mycourse.Count; i++)
{
if (i % 3 == 0) {
#:<tr></tr>
}
<div>
#Html.CheckBoxFor(x => x.mycourse[i].Ischecked)
<label>#Model.mycourse[i].CourseName</label>
#Html.HiddenFor(x => x.mycourse[i].CourseID)
#Html.HiddenFor(x => x.mycourse[i].CourseName)
</div>
}
</table>
<input type="submit" value="Submit" />
}
i am getting checkbox id,name and which course is checked now how can i add student with including these values to relationship table CourseStudent ?
You have problems with your database design. Remove ischecked field.Remember while you are designing entities, you shouldn't put fields that are for view. It is like that I make Reports table from my each query.
correct your models
public partial class Course
{
public Course()
{
this.Student = new HashSet<Student>();
}
public int CourseID { get; set; }
public string CourseName { get; set; }
public virtual ICollection<Student> Student { get; set; }
}
public partial class Student
{
public Student()
{
this.Course = new HashSet<Course>();
}
public int StudentID { get; set; }
public string StudentName { get; set; }
public virtual ICollection<Course> Course { get; set; }
}
Add viewmodels(You can use view models for showing custom data for your view)
You can read more
ViewModel
public class CourseViewModel
{
public Course course { get; set; }
public bool CheckBox { get; set; }
}
public class StudentCourseViewModel
{
public List<CourseViewModel> coursesVM { get; set; }
public Student student { get; set; }
}
Controller
public ActionResult index()
{
Entities db = new Entities();
List<CourseViewModel> courselist = new List<CourseViewModel>();
foreach (var item in db.Course.ToList())
{
courselist.Add(new CourseViewModel { course = item});
}
StudentCourceViewModel model = new StudentCourseViewModel(
{
student = new student(),
coursesVM = courselist }
};
return View(model);
}
[HttpPost]
public ActionResult Save(StudentCourseViewModel model)
{
Entities db = new Entities();
Student stdindb = db.Students.FirstorDefault(c => c.StudentName == model.student.StudentName);
if(stdindb == null)
{
stdindb = new student(){//your properties};
db.students.add(stdindb);
}
foreach (var item in model.coursesVM)
{
if (item.Ischecked)
{
stdindb.Course.Add(db.course.single(c=>c.CourseId == item.course.CourseId ));
}
}
db.SaveChanges();
return View();
}
View
#model SchoolCMS.Models.StudentCourseViewModel
#{
ViewBag.Title = "Select Course";
}
#using (Html.BeginForm("Save", "home", FormMethod.Post))
{
#Html.TextBoxFor(c=>c.student.StudentName)
<table>
#for (int i = 0; i < Model.coursesVM.Count; i++)
{
<tr>
<td>
#Html.CheckBoxFor(c => c.coursesVM[i].Ischecked)
#Html.HiddenFor(c => c.coursesVM[i].course.CourseID)
</td>
<td>#Html.DisplayFor(c => c.coursesVM[i].course.CourseName)</td>
</tr>
}
</table>
<input type="submit" value="Submit" />
}

Include query result to List

I have a list of Hospitals
Hospital model:
public class Hospital
{
[Key]
public int HospitalID { get; set; }
public string Name { get; set; }
public virtual ICollection<HospitalSpeciality> HospitalSpecialities { get; set; }
public virtual ICollection<UserHospital> UserHospitals { get; set; }
}
and I have users associated to hospital. Model:
public class UserHospital
{
[Key]
public int UserHospitalID { get; set; }
public int HospitalID { get; set; }
public Hospital Hospitals { get; set; }
public string Id { get; set; }
public ApplicationUser Users { get; set; }
}
I need, on my list, return also the number of the user's that are associated to the Hospital.
This query is easy if I have only 1 hospital, but in my scenario, I have a list of hospitals
Controller:
public ActionResult Index()
{
var result = db.Hospitals.ToList();
return View(result);
}
I really don't know how to include the result of the query (number of users) on my list.
My view:
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.HospitalID }) |
#Html.ActionLink("Details", "Details", new { id=item.HospitalID }) |
#Html.ActionLink("Delete", "Delete", new { id=item.HospitalID })
</td>
</tr>
}
It looks like you need to ensure the users are being returned in the query... Like so:
public ActionResult Index()
{
var result = db.Hospitals
.Include("UserHospitals")
.Include("UserHospitals.Users")
.ToList();
return View(result);
}
Even if they are virtual, lazy loading won't occur because you are accessing the property in your razor view. The Include is forcing Eager Loading.
Alternatively, expose another Dbset...
public DbSet<User> Users { get; set; }
Now you can select the following:
db.Users.Where(x => !x.active).ToList();
Let me know how you get on :)

The right way to bind the model to save multiple records to a table

I have a Menusettings pages which displays all the menuname from the database(Menutable).I want to save all the details into MenuRole table.For that I think I have to iterate over each menuitem and store the details in the MenuRole table(Is it the correct method?).But the problem is I am getting MenuList as null.So I couldnot iterate over the menulist .Also I am not sure how can i bind the checkbox value to the MenuRole table.
The view is
Model is
public class MenuRoleVM
{
public int? RoleId { get; set; }
public SelectList RoleList { get; set; }
public MenuRole MenuRole { get; set; }
public IEnumerable<Menu> MenuList { get; set; }
}
public partial class MenuRole
{
public int Id { get; set; }
public Nullable<int> MenuID { get; set; }
public Nullable<int> RoleID { get; set; }
public Nullable<System.DateTime> TransactionTime { get; set; }
public bool CanAdd { get; set; }
public bool CanEdit { get; set; }
public bool CanDelete { get; set; }
public bool CanView { get; set; }
public virtual Menu Menu { get; set; }
public virtual Role Role { get; set; }
}
public partial class Menu
{
public Menu()
{
this.MenuRoles = new HashSet<MenuRole>();
}
public int Id { get; set; }
public string MenuName { get; set; }
public string NavigateUrl { get; set; }
public Nullable<int> ParentID { get; set; }
public virtual ICollection<MenuRole> MenuRoles { get; set; }
}
Controller for binding the View
public ActionResult Add()
{
var _menuRoleVM = new MenuRoleVM
{
RoleList = new SelectList(_db.Roles.ToList(), "Id", "RoleName"),
MenuList = _db.Menus.Where(m => m.NavigateUrl != "#"),
MenuRole = new MenuRole()
};
return View(_menuRoleVM);
}
HTML Markup of View
#model SMS.Models.ViewModel.MenuRoleVM
#foreach (var item in Model.MenuList.Select((x, i) => new { Data = x, Index = i }))
{
<tr>
<td>
<input type="checkbox" class="minimal checkAll" />
</td>
<td>#item.Data.MenuName</td>
<td>
#Html.CheckBoxFor(m => m.MenuRole.CanAdd, new { #class = "minimal single" })
</td>
<td>
#Html.CheckBoxFor(m => m.MenuRole.CanEdit, new { #class = "minimal single" })
</td>
<td>
#Html.CheckBoxFor(m => m.MenuRole.CanDelete, new { #class = "minimal single" })
</td>
<td>
#Html.CheckBoxFor(m => m.MenuRole.CanView, new { #class = "minimal single" })
</td>
</tr>
}
Any help is highly appreciated?
You need view models that represent what you want to display/edit
public class MenuVM
{
public int ID { get; set; }
public string Name { get; set; }
public bool CanAdd { get; set; }
public bool CanEdit { get; set; }
public bool CanDelete { get; set; }
public bool CanView { get; set; }
}
public class MenuRoleVM
{
public int? RoleId { get; set; }
public SelectList RoleList { get; set; }
public List<MenuVM> MenuList { get; set; }
}
and in the view
#model MenuRoleVM
#using (Html.BeginForm())
{
#Html.DropDownListFor(m => m.RoleId, Model.RoleList)
for(int i = 0; i < Model.MenuList.Count; i++)
{
#Html.HiddenFor(m => m.MenuList[i].ID)
#Html.DisplayFor(m => m.MenuList[i].Name)
#Html.CheckBoxFor(m => m.MenuList[i].CanAdd)
#Html.CheckBoxFor(m => m.MenuList[i].CanEdit)
....
}
<input type="submit" ... />
}

Resources