I have two models and their tables. Restaurant:
public int Id { get; set; }
public string Name { get; set; }
public string Location { get; set; }
public string Image { get; set; }
[InverseProperty("Restaurant")]
[InverseProperty("Restaurant")]
public ICollection<Menu> Menus { get; set; }
And Menu:
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Image { get; set; }
public int RestaurantId { get; set; }
public virtual Restaurant Restaurant { get; set; }
And I created controller by using EntityFramework with views. I have Restaurant Index view which shows list of restaurants and it has to have menu button in every restaurant info. Also I have Menu Index view which shows menu items. And now my problem is to show menu items which relates to specific restaurant.
RestaurantIndex Actionresult:
public ActionResult Index()
{
var restaurants = db.Restaurants;
return View(restaurants.ToList());
}
MenuIndex ActionResult:
public ActionResult Index()
{
var menus = db.Menus.Include(m => m.Restaurant);
return View(menus.ToList());
}
That my restaurant's list:
enter image description here
And it is menu:
but it shows all items
I searched about eager loading, but couldn't find similar examples. Can someone tell me how to realize it?
From looking at the code an the 'images' you have. I would simply pass the Id of the restaurant as nullable int? into the Index of the MenuController such as:
public ActionResult Index(int? restaurantId = null)
{
List<Menu> menus = new List<Menu>();
if(menuId != null){
menus = db.Menus.Include(m => m.Restaurant).Where(x => RestaurantId == restaurantId);
}
else{
menus = db.Menus.Include(m => m.Restaurant)
}
return View(menus.ToList());
}
Then above we filter against the restaurant ID in the controller instead of the view.
In the menu view you should not have to change anything you have done so far.
By having the ID as a nullable parameter, this Index method can be called using an ID or not.
Related
I've been struggling with this for a bit, and can't seem to find a solution.
I have a Picker that displays a list of college terms. Binding it isn't the issue.
I also have a table of college courses. Each course needs to be attached to a specified term.
I have set the SelectedItem to a Property of the Terms table through the SelectedIndexChanged event.
On a separate page, I want to display the SelectedItem from the previous page and the list of college course attached to that term. Right now, the new page displays all the terms (with the name changed to the SelectedItem, and all the courses, regardless of which term they are attached to (through TermId).
I figured this would need to be done with a SQLite query using an inner join, but I can't seem to set the ListView ItemsSouce to that results of that query (even using ToList();), and am not even sure if thats something I should be doing.
I also thought about creating a new table, and inserting the values of the query into that table and binding it that way.
Any advice on the best way to accomplish this?
Terms table:
public class Terms
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string TermName { get; set; }
public string TermStatus { get; set; }
public DateTime TProjStart { get; set; }
public DateTime TProjEnd { get; set; }
public DateTime TActStart { get; set; }
public DateTime TActEnd { get; set; }
public string Pick { get; set; }
}
Courses Table:
public class Courses
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string CourseName { get; set; }
public string CourseStatus { get; set; }
public DateTime CourseStart { get; set; }
public DateTime CourseEnd { get; set; }
public string InstructName { get; set; }
public string InstructEmail { get; set; }
public string InstructPhone { get; set; }
public string Notes { get; set; }
public int TermId { get; set; }
}
The TermId is supposed to reference Terms.Id as a sort of foreignkey, but I can't seem to actually make it a foreignkey, so I was thinking I may need to manually set it.
Picker SelectedIndexChanged event:
public void termPicker_SelectedIndexChanged(object sender, EventArgs e)
{
using (SQLiteConnection conn = new SQLiteConnection(App.DatabaseLocation))
{
var pick = (Terms)termPicker.SelectedItem;
if (termPicker.SelectedItem != null)
{
conn.Execute($"UPDATE Terms SET Pick ='{pick.TermName}'");
}
else
{
var refresh = new Page1();
Navigation.InsertPageBefore(refresh, this);
Navigation.PopAsync();
}
Now just need to figure out a way to display only the courses attached
to the selected term
var courses = db.Table<Courses>().Where(c => c.TermId == selectedTerm).ToList();
I am following this course, and the instructor added code to add records to the DB. The class that's going to be added to the DB looks like this:
public class Gig
{
public int Id { get; set; }
public ApplicationUser Artist { get; set; }
[Required]
public string ArtistId { get; set; }
public DateTime DateTime { get; set; }
[Required]
[StringLength(255)]
public string Venue { get; set; }
public Genre Genre { get; set; }
[Required]
public byte GenreId { get; set; }
}
And there's a view model that's attached to the view, to do the mapping, and it looks like this:
public class GigFormViewModel
{
[Required]
public string Venue { get; set; }
[Required]
[FutureDate]
public string Date { get; set; }
[Required]
[ValidTime]
public string Time { get; set; }
[Required]
public byte Genre { get; set; }
[Required]
public IEnumerable<Genre> Genres { get; set; }
public DateTime GetDateTime() => DateTime.Parse($"{Date} {Time}");
}
I have a create method, that gets form fields, and does the mapping from view model to the model itself, and then tries to add the records to the DB, my create action looks like this:
[Authorize]
[HttpPost]
public ActionResult Create(GigFormViewModel viewModel)
{
viewModel.Genres = _context.Genres.ToList();
if (!ModelState.IsValid)
{
return View(viewModel);
}
var gig = new Gig()
{
GenreId = viewModel.Genre,
ArtistId = User.Identity.GetUserId(),
DateTime = viewModel.GetDateTime(),
Venue = viewModel.Venue
};
_context.Gigs.Add(gig);
_context.SaveChanges();
return RedirectToAction("Index", "Home");
}
On the submit, I get the viewModel's property Genres and populate it with the records from the DB, then I check for ModelState.IsValid. but it's giving me:
Genres Field is required
Although I set it just 2 lines above.
Is there something wrong am doing here? Any guidance is appreciated.
Thanks.
You don't need to add a validation data annotation to Genres property in your view model because it is not a data that is set by users but it is set by you to help your view to get a collection of Genre and populate something like a dropdown list.
Firstly, remove the [Required] attribute that decorates your Genres property in GigFormViewModel.
Secondly, refactor your action method, specially the if bloc like below:
[Authorize]
[HttpPost]
public ActionResult Create(GigFormViewModel viewModel)
{
if (!ModelState.IsValid)
{
// re-populate Genres collection only is tha data is in invalid state.
viewModel.Genres = _context.Genres.ToList();
return View(viewModel);
}
// The remainder code does not change
}
I have entities that may have children and their children may have children and so on...
When I get database models all entities are OK with correct children and parent. But the problem comes when I want to map to view model:
Is there any possible way to map models from database like this?
// database model code first
public class Tomato
{
public int Id { get; set; }
public string Name { get; set; }
public int? ParentId { get; set; }
public virtual Tomato Parent { get; set; }
public ICollection<Tomato> Children { get; set; }
}
// mvc view model
public class TomatoViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public int? ParentId { get; set; }
public ICollection<TomatoViewModel> Children { get; set; }
}
Configured configuration.CreateMap<Tomato, TomatoModel>() but throws an StackOverflowException when try to bind child elements. Tried with
configuration.CreateMap<Tomato, TomatoViewModel>().ForMember( t => t.Children,
options => options.Condition(context => (context.SourceValue as Tomato).ParentId == this.Id));
// this.Id refers to TomatoViewModel.Id
Update: in my controller class:
var models = foodIngredientsService.GetAllTomatoes().Where(t => t.ParentId == null).To<TomatoModel>().ToList();
Second Question: how to use second overload of options.Condition(Func<TomatoModel, bool> func) ??
Try this where you specify the child member mapping;
configuration.CreateMap<Tomato, TomatoViewModel>()
.ForMember(t => t.Children, options => options.MapFrom(source => source.Children));
I have a two relational Model first one is
Teacher.cs
public class Teachers
{
[Key]
public int TeacherID { get; set; }
public string TeacherName { get; set; }
public string TeacherLname { get; set; }
public int DepartmentID { get; set; }
public string Image { get; set; }
public Department Department { get; set; }
}
and second is Department.cs
public int DepartmentID { get; set; }
public string DepartmentName { get; set; }
public string Image { get; set; }
public List<Teachers> Teachers { get; set; }
When I'm creating a new record, I' choose a Department Name for teacher, and It's adding fine. But When I want to Delete a record there is a error like this
The ViewData item that has the key 'DepartmentID' is of type 'System.Int32' but must be of type 'IEnumerable<SelectListItem>'.
Line 32: #Html.DropDownList("DepartmentID", String.Empty)
I don't understand what I need to do. Can you help me?
Thanks a lot
TeacherController
EDIT :
//
// GET: /Teachers/Delete/5
[Authorize(Roles = "A")]
public ActionResult Delete(int id = 0)
{
Teachers teachers = db.Teachers.Find(id);
if (teachers == null)
{
return HttpNotFound();
}
return View(teachers);
}
//
// POST: /Teachers/Delete/5
[Authorize(Roles = "A")]
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
Teachers teachers = db.Teachers.Find(id);
db.Teachers.Remove(teachers);
db.SaveChanges();
return RedirectToAction("Index");
}
When you pass an empty string into Html.DropDownList() it looks for a list of items to populate the dropdownlist from the first parameter in the ViewData collection. However, there is already an item in that collection that is of type Int32.
This is one of the many confusing scenarios that happen when you use Html.DropDownList() rather than using a strongly typed model and Html.DropDownListFor()
I suggest you do this:
#Html.DropDownListFor(x => x.DepartmentID, Model.Departments)
You will need to populate your model with a Departments object that is a list of Departments
This question is in reference to the project discussed here. After resolving the previous problem I have run into a new one. When The Student object is saved, the list of courses associated with it is not saved. I can see the collection of course objects when I mouse over the student object after setting a breakpoint:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AddCourseVM (AddCourseViewModel vModel)
{
Student stu = db.Students.Find(vModel.Student.ID);
foreach (Course c in vModel.PossibleCourses)
{
if (c.Selected)
{
BaseCourse bc = db.BaseCourses.Find(c.BaseCourse.ID);
c.BaseCourse = bc;
c.Student = stu;
stu.CoursesTaken.Add(c);
}
}
if (stu != null)
{
db.Entry(stu).State = EntityState.Modified; //breakpoint here
db.SaveChanges();
}
return RedirectToAction("ListTakenCourses", stu);
}
public ActionResult ListTakenCourses (Student stu)
{
List<Course> taken = stu.CoursesTaken.ToList();
foreach (Course c in taken)
{
c.BaseCourse = db.BaseCourses.Find(c.BaseCourse.ID);
}
ViewBag.CoursesTaken = taken;
return View(stu);
}
But when I pass the object to the next method, the list of courses taken comes back null. The courses are being saved to the database, I can see them when I go into the SQL Server explorer, but for some reason they are not being attached to the student object. The code for the objects:
public class Student
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string WNumber { get; set; }
public int HoursCompleted { get; set; }
public double GPA { get; set; }
public Concentration StudentConcentration { get; set; }
public virtual ICollection<Course> CoursesTaken { get; set; }
public virtual ICollection<Course> CoursesRecommended { get; set; }
}
and:
public class Course
{
public int ID { get; set; }
public string Semester { get; set; }
public Grade? Grade { get; set; }
public bool Selected { get; set; }
public BaseCourse BaseCourse { get; set; }
public Student Student { get; set; }
}
Something that may be important, but that I don't really understand: when I look at the table for the Course object in the database, there are three columns, called Student_ID, Student_ID1, and Student_ID2. I assume they relate to the student associated with the object and the two ways it can be associated (recommended or taken), but the odd thing is that Student_ID is always null, while the other two sometimes have a value and sometimes do not. I have not even begun to implement the recommendation process, so there is no way that list is being filled.
I reworked the classes and now it seems to be working. I changed the Course object to:
public class Course
{
public int ID { get; set; }
public string Semester { get; set; }
public Grade? Grade { get; set; }
public bool Selected { get; set; }
public int BaseCourseID { get; set; }
public int StudentID { get; set; }
public BaseCourse BaseCourse { get; set; }
public Student Student { get; set; }
}
and the controller methods to:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AddCourseVM (AddCourseViewModel vModel)
{
Student stu = db.Students.Find(vModel.Student.ID);
foreach (Course c in vModel.PossibleCourses)
{
if (c.Selected)
{
BaseCourse bc = db.BaseCourses.Find(c.BaseCourse.ID);
c.BaseCourse = bc;
c.Student = stu;
stu.CoursesTaken.Add(c);
db.Entry(c).State = EntityState.Added;
}
}
if (stu != null)
{
db.Entry(stu).State = EntityState.Modified;
db.SaveChanges();
}
return RedirectToAction("ListTakenCourses", stu);
}
public ActionResult ListTakenCourses (Student stu)
{
List<Course> taken = db.Courses.Where(c => c.StudentID == stu.ID).ToList();
foreach (Course c in taken)
{
c.BaseCourse = db.BaseCourses.Find(c.BaseCourseID);
c.Student = stu;
stu.CoursesTaken.Add(c);
}
ViewBag.CoursesTaken = taken;
return View(stu);
}
And it is now displaying the courses I add on the next page, but it seems odd that I have to save the child objects separately from the parent and that I have to get the list from the database manually instead of being able to use the object structure. Is this intended behavior, or is there a better way of doing what I'm trying to do (add a list of child objects (courses) to a student object, save the relationship to the database, and then display the list of added objects)?
You are not "passing the object to the next method". You are serializing the object and passing it on the URL, then deserializing it on the other end with this method:
return RedirectToAction("ListTakenCourses", stu);
This is not the way to go about things. What you should be doing is passing a single id, such as the student id. Then, in ListTakenCourses you look up the student again in the database, which if you are doing your query correctly will fully populate the objects.
return RedirectToAction("ListTakenCourses", new { id = stu.StudentID });
public ActionResult ListTakenCourses (int id)
{
List<Course> taken = db.Courses.Where(c => c.StudentID == id).ToList();
//...
}