MVC Using relationships Database first - model-view-controller

First off I have a list of movies setup from a database working fine on localhost/movies
However I wanted to go a step further so I created an Actors page, I now list all the Actors to ( On their own just their names )
What I want to do is you have for example -
Leonardo DiCaprio (id = 1)
I would like to check the page to see if the id matches him etc ( This is already done as you will see in code below )
Next I would like All his movies he has been in to be shown in another row on the table. As you can see VIA This screenshot
http://gyazo.com/ae193d80e7a39969116f76ab6568f38e.png
Instead of just the movies he has starred in they all show up, as you can see below I made a relationship between tables Actor & Movie & ActorsMovies, just linking the Ids
I have 3 tables setup like the following:
Movies -
Id (PK),
Name
Actor:
Id(PK),
Name
ActorsInMovies:
MovieId(PK),
ActorId(PK)
Here is my controller:
public ActionResult Movies( int id )
{
var model = MoviesViewModel(); //Create our model
model.PageTitle = = actor.Name + "'s' Movies"; // set page title to actors name
model.Actorname = actor.Name; // I do this to ensure the name always matches the id
var items = db.Movies.OrderBy(i => i.Name).AsQueryable(); //Link items to Movies DB and make it queryable ( as I want to use pagedLists later when its working )
if (!String.IsNullOrEmpty(actor.Name)) //if name is not null
{
items = items.Where( a => a.Id == id );//I already know this is wrong but I dont know what the correct process is I think the theory part behind it I understand, here I need to check the relationships to ensure that the actor matches with the movies I just am unsure how to do it.
}
model.MovieList = items.ToList();
return View(model);
}
and my views is just a normal table with foreach loop in ( im gonna remove the Html so it doesnt get to messy:
#foreach (var item in Model.MovieList)
{
<tr>
<td>#Html.DisplayFor(modelItem => Model.Actorname)</td>
<td>
<!-- MS:This part populates the table row-->
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.Id }, new { #class = "btn btn-default" })
#Html.ActionLink("Details", "Details", new { id = item.Id }, new { #class = "btn btn-default" })
#Html.ActionLink("Delete", "Delete", new { id = item.Id }, new { #class = "btn btn-danger" })
</td>
</tr>
}

The easiest way to map many-to-many relationships in MVC and to also limit the number of database queries, is like below. This also does pagination, since you mentioned the desire.
MovieActor schema: (many-to-many map of Movie and Actor)
ID (PK)
ActorID (FK to Actor.ID)
MovieID (FK to Movie.ID)
CharacterName
Controller:
const int Rows = 50; // put this wherever appropriate, or customize and save in Session
/// <summary>
/// This shows all the Movies the given Actor was in.
/// </summary>
/// <param name="id">Actor ID</param>
/// <param name="page">Page of Movies to display, starting at 1</param>
public ActionResult Movies(int id, int page = 1)
{
// get the actor
Actor actor = MyDataContext.GetActorByID(id); // one database call
// get the actor's movies
List<Movie> movies = MyDataContext
.GetMoviesByActorID(actor.id) // no database call
.Skip((page - 1) * Rows)
.Take(Rows); // one database call
// build the view model
// NOTE: could move this into a constructor MoviesViewModel(actor, movies)
var viewModel = MoviesViewModel
{
PageTitle = actor.Name + "'s Movies",
ActorName = actor.Name,
Movies = movies
};
// show the view
return View(model);
}
Model:
public partial class MyDataContext
{
public Actor GetActorByID(int id)
{
return Actors.FirstOrDefault(x => x.ID == id)
}
public IQueryable<List<Movie>> GetMoviesByActorID(int actorID)
{
return Movies.Where(x => x.MovieActor.ActorID == actorID);
}
}
And the view is like any normal IEnumerable or List.

Related

I can't fetch data from the db with a Html.DropDownListFor, in MVC 4 (or 5)

I have read some pages regarding DDls, but I can't get my DDLF (DropDownListFor) to work. I have a model, and a db, but I don't know how I can in the view show one DDLF.
What I get to work, is this code:
#foreach (var item in Model)
{
#Html.DropDownListFor(m => item.Id,
new SelectList(ViewBag.SjukhusDropDownList),
"Choose something")
}
But then I get several DDLs. And I don't know how to insert data into them, so the data to the user will get fetched from the db. Like sending an id with something to the action, to do something with it. But here, I can only click on anything in the list, but of course, nothing will happen since I haven't bound any data to any option in that select list.
Here's an example for DropDownListFor in MVC 4
In the model create two properties, one for the list itself and one for identifying the list entries. In this example the vu-number identifies a merchant:
public class MyModel
{
[Display(Name = "VU")]
public string vu{ get; set; }
public List<SelectListItem> merchantList{ get; set; }
}
Initialize and load model data in the Http GET method of the controller:
public ActionResult ShowMerchants()
{
MyModel model = new MyModel();
model.merchantList = GetMerchants();
model.vu = model.merchantList[0].Value;
return View(model);
}
Create the list items using some rows from database. I.e. you can use TableAdapter and DataTable to achieve it:
private List<SelectListItem> GetMerchants()
{
List<SelectListItem> merchantList = new List<SelectListItem>();
MyDataSet.viewVUDataTable myDataTable = new MyDataSet.viewVUDataTable();
MyDataTableAdapters.VUListTableAdapter myTableAdapter = new MyDataTableAdapters.VUListTableAdapter();
myTableAdapter.Fill(myDataTable);
// iterate over rows in datatable
for(int i=0; i< myDataTable.Rows.Count; i++)
{
// Text is shown in the dropdownlist
// Value is used to identify the selected item
merchantList.Add(
new SelectListItem { Text = myDataTable[i].text, Value = myDataTable[i].vu});
}
return merchantList;
}
and in the view:
#Html.DropDownListFor(
model => model.vu,
Model.merchantList,
new { #class = "form-control" })

MVC How to pass a list of objects with List Items POST action method

I want to post a List of items to controller from Razor view , but i am getting a List of objects as null
My class structre is
Model:
List<Subjects> modelItem
class Subjects
{
int SubId{get;set;}
string Name{get;set;}
List<Students> StudentEntires{get;set;}
}
class StudentEntires
{
int StudId{get;set;}
string Name{get;set;}
int Mark{get;set;}
}
The model itself is a list of items and every items contain List of child items as well. Example model is a list of Subjects and every subject contains a List of Students, and i want to input mark for every student
My View is like
#model IList<Subjects>
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
if (Model.Count > 0)
{
#for (int item = 0; item < Model.Count(); item++)
{
<b>#Model[item].Name</b><br />
#foreach (StudentEntires markItem in Model[item].StudentEntires)
{
#Html.TextBoxFor(modelItem => markItem.Mark)
}
}
<p style="text-align:center">
<input type="submit" class="btn btn-primary" value="Update" />
</p>
}
}
And in controller
[HttpPost]
public ActionResult OptionalMarks(int Id,ICollection<Subjects> model)
{
//BUt my model is null. Any idea about this?
}
You're finding this difficult because you're not utilising the full power of the MVC framework, so allow me to provide a working example.
First up, let's create a view model to encapsulate your view's data requirements:
public class SubjectGradesViewModel
{
public SubjectGradesViewModel()
{
Subjects = new List<Subject>();
}
public List<Subject> Subjects { get; set; }
}
Next, create a class to represent your subject model:
public class Subject
{
public int Id { get; set; }
public string Name { get; set; }
public List<Student> StudentEntries { get; set; }
}
Finally, a class to represent a student:
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int Grade { get; set; }
}
At this point, you have all the classes you need to represent your data. Now let's create two controller actions, including some sample data so you can see how this works:
public ActionResult Index()
{
var model = new SubjectGradesViewModel();
// This sample data would normally be fetched
// from your database
var compsci = new Subject
{
Id = 1,
Name = "Computer Science",
StudentEntries = new List<Student>()
{
new Student { Id = 1, Name = "CompSci 1" },
new Student { Id = 2, Name = "CompSci 2" },
}
};
var maths = new Subject
{
Id = 2,
Name = "Mathematics",
StudentEntries = new List<Student>()
{
new Student { Id = 3, Name = "Maths 1" },
new Student { Id = 4, Name = "Maths 2" },
}
};
model.Subjects.Add(compsci);
model.Subjects.Add(maths);
return View(model);
}
[HttpPost]
public ActionResult Index(SubjectGradesViewModel model)
{
if (ModelState.IsValid)
{
return RedirectToAction("Success");
}
// There were validation errors
// so redisplay the form
return View(model);
}
Now it's time to construct the views, and this part is particularly important when it comes to sending data back to a controller. First up is the Index view:
#model SubjectGradesViewModel
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
#Html.EditorFor(m => m.Subjects) <br />
<input type="submit" />
}
You'll notice I'm simply using Html.EditorFor, whilst passing Subjects as the parameter. The reason I'm doing this is because we're going to create an EditorTemplate to represent a Subject. I'll explain more later on. For now, just know that EditorTemplates and DisplayTemplates are special folder names in MVC, so their names, and locations, are important.
We're actually going to create two templates: one for Subject and one for Student. To do that, follow these steps:
Create an EditorTemplates folder inside your view's current folder (e.g. if your view is Home\Index.cshtml, create the folder Home\EditorTemplates).
Create a strongly-typed view in that directory with the name that matches your model (i.e. in this case you would make two views, which would be called Subject.cshtml and Student.cshtml, respectively (again, the naming is important)).
Subject.cshtml should look like this:
#model Subject
<b>#Model.Name</b><br />
#Html.HiddenFor(m => m.Id)
#Html.HiddenFor(m => m.Name)
#Html.EditorFor(m => m.StudentEntries)
Student.cshtml should look like this:
#model Student
#Html.HiddenFor(m => m.Id)
#Html.HiddenFor(m => m.Name)
#Html.DisplayFor(m => m.Name): #Html.EditorFor(m => m.Grade)
<br />
That's it. If you now build and run this application, putting a breakpoint on the POST index action, you'll see the model is correctly populated.
So, what are EditorTemplates, and their counterparts, DisplayTemplates? They allow you to create reusable portions of views, allowing you to organise your views a little more.
The great thing about them is the templated helpers, that is Html.EditorFor and Html.DisplayFor, are smart enough to know when they're dealing with a template for a collection. That means you no longer have to loop over the items, manually invoking a template each time. You also don't have to perform any null or Count() checking, because the helpers will handle that all for you. You're left with views which are clean and free of logic.
EditorTemplates also generate appropriate names when you want to POST collections to a controller action. That makes model binding to a list much, much simpler than generating those names yourself. There are times where you'd still have to do that, but this is not one of them.
Change the action method signature to
public ActionResult OptionalMarks(ICollection<Subjects> model)
Since in your HTML, it does not look like there is anything named Id in there. This isn't your main issue though.
Next, do the following with the foor loop
#for(int idx = 0; idx < Model[item].StudentEntires.Count();idx++)
{
#Html.TextBoxFor(_ => Model[item].StudentEntries[idx])
}
Possibly due to the use of a foreach loop for the StudentEntries, the model binder is having trouble piecing everything together, and thus a NULL is returned.
EDIT:
Here's an example:
Controller
public class HomeController : Controller
{
public ActionResult Index()
{
var viewModel = new IndexViewModel();
var subjects = new List<Subject>();
var subject1 = new Subject();
subject1.Name = "History";
subject1.StudentEntires.Add(new Student { Mark = 50 });
subjects.Add(subject1);
viewModel.Subjects = subjects;
return View(viewModel);
}
[HttpPost]
public ActionResult Index(IndexViewModel viewModel)
{
return new EmptyResult();
}
}
View
#model SOWorkbench.Controllers.IndexViewModel
#{
ViewBag.Title = "Home Page";
}
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
if (Model.Subjects.Any())
{
int subjectsCount = Model.Subjects.Count();
for (int item = 0; item < subjectsCount; item++)
{
<b>#Model.Subjects[item].Name</b><br />
int studentEntriesCount = Model.Subjects[item].StudentEntires.Count();
for(int idx = 0;idx < studentEntriesCount;idx++)
{
#Html.TextBoxFor(_ => Model.Subjects[item].StudentEntires[idx].Mark);
}
}
<p style="text-align:center">
<input type="submit" class="btn btn-primary" value="Update" />
</p>
}
}
When you post the form, you should see the data come back in the viewModel object.

ASP.NET MVC 4 Want to populate dropdown list from database

I am new guy in ASP.NET MVC 4. I want to populate dropdownlist from database table BO where Column name is Id, Code, Name, OrgId. I want to bind two Code & Namecolumn's data to DataTextfield and Id column Data to DataValueField of dropdown. I have created code for this which are as follows BUT ITS NOT RETURNING DATA FROM TABLE and var BOList is remain empty :
my connectionstring is
<add name="iRegDBContext"
connectionString="Data Source=****;Initial Catalog=iReg;User ID=**;Password=****;Integrated Security=True"
providerName="System.Data.SqlClient"
/>
My Controller class :
public class iRegController : Controller
{
private iRegDBContext l_oDbBO = new iRegDBContext();
// GET: /iReg/
public ActionResult PopulatejQgrid()
{
var BOList = l_oDbBO
.BO
.ToList()
.Select(d => new SelectListItem
{
Value = d.Id.ToString(),
Text = d.Name + "[ " + d.Code + " ]"
});
ViewBag.BOData = new SelectList(BOList, "Value", "Text");
return View();
}
}
My Model class :
public class BO
{
public Guid Id { get; set; }
public string Code { get; set; }
public string Name { get; set; }
}
public class iRegDBContext : DbContext
{
public DbSet<BO> BO { get; set; }
}
My cshtml class :
#model MvciReg.Models.BO
#{
ViewBag.Title = "PopulatejQgrid";
}
#using (Html.BeginForm())
{
<fieldset>
BO :
#Html.DropDownList("BOData")
<p>
<input type="submit" value="Go" />
</p>
</fieldset>
}
I really don't know where I am going wrong. I developed my code from reference of following link Click here . Kindly suggest correction in code ...
UPDATE: I tried following Matt Bodily's code in my controller and what I see is code is not fetching data from database and that code is
public ActionResult populatejQgrid()
{
ViewBag.BOData = GetDropDown();
return View();
}
public static List<SelectListItem> GetDropDown()
{
List<SelectListItem> ls = new List<SelectListItem>();
var lm = from m in db.BOs //fetch data from database
select m;
foreach (var temp in lm)
{
ls.Add(new SelectListItem() { Text = temp.Name, Value = temp.Id.ToString() });
}
return ls;
}
In Controller :
#Html.DropDownList("BOData", (List<SelectListItem>)ViewBag.BOData)
But when I saw value of ls through watch it always show me Count = 0 but its not giving me any error.
I found something new this problem. When I kept mouse pointer over var lm; it shows me query and in query table name in FROM clause is not that one in my SQL database. My SQL table name is BO and in query it is taking BOes. I don't know from where this name is coming. I think this is the main cause of all this problem So How I overcome this??
First Create a BO list for Dropdownlist in VIEW
#{
var Bolst= Model.BO.Select(cl => new SelectListItem
{
Value = cl.Value.ToString(),
Text = cl.Text== null ? String.Empty : cl.Text
});
}
#(Html.DropDownList("sampleDropdown", BOlst, "-----Select-----"))
In Controller:
return View(BOlst); // why use Viewbag when directly pass it to view
from what I see in your code you are creating the select list and setting the ViewBag.BOData on the controller.
So in order to render it on the view you should do this
#Html.DropDownList(ViewBag.BOData)
instead of
#Html.DropDownList("BOData")
Regarding the access to the database are you trying to use "code first" in an existing database?
If you are you need to override the context constructor like this
public class iRegDBContext : DbContext
{
  public iRegDBContext()
     :base("Name= iRegDBContext")
   {
   }
}
see this link http://msdn.microsoft.com/en-us/data/jj200620.aspx
Hope it helps.
try building your dropdown this way
#Html.DropDownList(x => x.Selected, PathToController.GetDropDown())
and then in your controller
public static List<SelectListItem> GetDropDown()
{
List<SelectListItem> ls = new List<SelectListItem>();
lm = (call database);
foreach (var temp in lm)
{
ls.Add(new SelectListItem() { Text = temp.name, Value = temp.id });
}
return ls;
}
Hopefully this helps
I recently had this issue also and managed to get it working using Viewbag. You will need to make it fit your Db tables but it works and is quite simple.
Populating Drop Down Box with Db Data

Incorporating custom view model data from multiple tables into a view

I can't understand why I can't find anything that clearly explains how to do this with MVC. Seems like a pretty routine issue:
I have three tables:
PackageName: PackageNameID,PackageName
Food: FoodID,Food
PackageContent: PackageContentID, PackageNameID, FoodID, Qty
The application is supposed to describe packages of food. For example, a package named "A" might contain 4 onions and 3 peppers. Another package, "B", might contain 2 rolls and 2 onions.
Currently I have a custom view model ("PackageNameModel") that collects the data:
public ViewResult Index() {
var viewModel =
from pn in db.PackageNames
from pc in db.PackageContents
.Where(p => p.PackageNameID == pn.PackageNameID).DefaultIfEmpty()
from f in db.Foods
.Where(f => f.FoodID == pc.FoodID).DefaultIfEmpty()
select new PackageFoodModel { PackageName = pn, PackageContent = pc, Food = f };
return View( viewModel );
}
This returns the data correctly, but what do I do with it so that the view is actually one that is useful for the application?
Using this in my view:
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.PackageName.PackageName1 ) ,
#Html.DisplayFor(modelItem => item.Food.Food1)=#Html.DisplayFor(modelItem => item.PackageContent.Qty)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.PackageName.PackageNameID }) |
#Html.ActionLink("Details", "Details", new { id = item.PackageName.PackageNameID }) |
#Html.ActionLink("Delete", "Delete", new { id = item.PackageName.PackageNameID })
</td>
</tr>
}
I am able to display the following:
- A , Onions=4 Edit | Details | Delete
- A , Peppers=3 Edit | Details | Delete
- B , Rolls=2 Edit | Details | Delete
- B , Onions=2 Edit | Details | Delete
This is not very useful. What I'm trying to do is display something like this:
- A (Onions=4,Peppers=3) Edit | Details | Delete
- B (Rolls=2,Onions=2) Edit | Details | Delete
Eventually, the next page down after navigating to the "Edit" action would provide an editable name for the package as well as a table of all available foods and an adjacent quantity box so that the foods/quantities within each package may be updated.
Can anyone explain how this is done within the MVC framework?
All the tutorials I have seen on the web/in books deal with data that is much simpler. Can anyone point me to some sample code / tutorials that deal with something like this?
Thanks in advance!
You need to aggregrate your results.
Create a view model like this
public class PackageViewModel
{
public int ID { set;get;}
public string Name { set;get;}
public List<FoodItem> FoodItems { set;get;}
public PackageViewModel()
{
FoodItems=new List<FoodItem>();
}
}
public class FoodItem
{
public string FoodName { set;get;}
public int Quantity { set;get;}
}
and in your Get action, You need to do the aggregate the data from your data which is ccoming from your data access layer and fill to the ViewModel List
public ActionResult IndeX()
{
List<PackageViewModel> listVM=new List<PackageViewModel>();
//Get your data and do aggregation and fill in listVM
return View(listVm)l
}
and in our view,
#model List<PackageViewModel>
#foreach(var item in Model)
{
<tr>
<td>#item.Name</td>
<td>
foreach(var food in item.FoodItems)
{
#food.FoodName - #food.Quantity
}
</td>
<td>
#Html.ActionLink("Edit","Edit",new {#id=item.ID})
</td>
</tr>
}

How do you properly create a MultiSelect <select> using the DropdownList helper?

(sorry, there are several item here but none seems to allow me to get this working.)
I want to create a DropDownList which allows multiple selection. I am able to populate the list but I can't get the currently selected values to seem to work.
I have the following in my controller:
ViewBag.PropertyGroups = from g in db.eFinGroups
where g.GroupType.Contents == "P"
select new
{
Key = g.Key,
Value = g.Description,
Selected = true
};
ViewBag.SelectedPropertyGroups = from g in company.Entities
.First().Properties.First().PropertyGroups
select new {
g.eFinGroup.Key,
Value = g.eFinGroup.Description };
In the view I have:
#Html.DropDownListFor(model => model.PropertyGroupsX,
new MultiSelectList(ViewBag.PropertyGroups
, "Key", "Value"
, ViewBag.SelectedPropertyGroups),
new { #class = "chzn-select", data_placeholder = "Choose a Property Group", multiple = "multiple", style = "width:350px;" })
PropertyGroupX is a string[] in the model.
I have tried all types of iterations with the selected properties... passing just the value, just the key, both, etc.
Also, what type is PropertyGroupX supposed to be? Is string array correct? Or should it be a dictionary that contains the current propertygroups? I really am having a hard time finding doc on this.
Someone suggested I should be using ListBoxFor. I have changed to that and still have the same issue. The selected values are not being set as selected when the option tags are rendered. Here is what I have tried:
#Html.ListBoxFor(model => model.PropertyGroups, new MultiSelectList(ViewBag.PropertyGroups, "Key", "Value"))
I have tried the model.PropertyGroups as a collection of string matching the Values, as a collection of Guid matching this IDs and as an anonymous type with both a Key and Value to match the items in the ViewBag. Nothing seems to work.
You don't use DropDownListFor if you want to create a multiselect list. You use the ListBoxFor helper.
View model:
public class MyViewModel
{
public string[] SelectedIds { get; set; }
public IEnumerable<SelectListItem> Items { get; set; }
}
Controller:
public ActionResult Index()
{
var model = new MyViewModel
{
// preselect the first and the third item given their ids
SelectedIds = new[] { "1", "3" },
// fetch the items from some data source
Items = Enumerable.Range(1, 5).Select(x => new SelectListItem
{
Value = x.ToString(),
Text = "item " + x
})
};
return View(model);
}
View:
#model MyViewModel
#Html.ListBoxFor(x => x.SelectedIds, Model.Items)

Resources