MVC passing object from model through controller to view - asp.net-mvc

I don't have much experience with programming and I'm new to MVC.
I want to fetch some data from database with entity framework and print it in the view.
This is my model:
public class Grad
{
public int ID { get; set; }
public string Naziv { get; set; }
public char KoordinataX { get; set; }
public char KoordinataY { get; set; }
public int BrojStanovnika { get; set; }
}
public class GradDBContext : DbContext
{
public DbSet<Grad> Gradovi { get; set; }
}
this is a controller:
private GradDBContext db = new GradDBContext();
public ActionResult Index()
{
List<int> gradoviList = new List<int>();
foreach (sea.Models.Grad g in db.Gradovi)
{
gradoviList.Add(g.ID);
}
ViewData["Gradovi"] = new SelectList(gradoviList);
return View();
}
and this is a view:
#foreach (var item in ViewData["Gradovi"] as IEnumerable<int>) ---> error appears here as null reference exception
{
<p>item</p>
}
I know that I have to parse data but don't have idea what did I do wrong

The ViewData item with the key "Gradovi" is typeof SelectList, so it would need to be
#foreach (var item in ViewData["Gradovi"] as SelectList)
{
<p>#item.Value</p> // or #item.Text
However there is no point generating IEnumerable<SelectListItem> (which is what SelectList is) when you do not need it, and you should be passing your model to the view. Your code in the controller should be
public ActionResult Index()
{
IEnumerable<int> model = db.Gradovi.Select(x => x.ID);
return View(model);
}
and in the view
#model IEnumerable<int>
#foreach(var item in Model)
{
<p>#item</p>
}

Your code can work like you have it, but I am going to modify it a bit and give you some pointers. I am supplying an answer based on what I see in your post, not what I think you want to achieve at a later stage. There are many ways to accomplish a goal, I will select the simplest way that I will normally use:
public ActionResult Index()
{
// You will have a repository layer for this part
GradDBContext db = new GradDBContext();
// Get a list of your items
List<Grad> gradovis = db.Gradovi.ToList();
// I never work with view data, I just pass my view model to the view
// This way you now have more data to display on the screen (if you need more)
return View(gradovis);
}
And then your view could look like this:
#model List<Project.Model.Grad>
#foreach (var grad in Model)
{
<p>#grad.ID</p>
}

Related

How to display a list in View in MVC?

Ok, so I have seen a lot of similar questions, but unfortunalety I can't figure out how to access a list of users from db in the View. I get a System.NullReferenceException. Can one of you experts see what I am doing wrong?
Model
public class MyModel
{
[Key]
public int Id { get; set; }
[Display(Name = "Name")]
public string Name { get; set; }
public bool TriggerOnLoad { get; set; }
public string TriggerOnLoadMessage { get; set; }
public string EmployeeNumber { get; set; }
public IEnumerable<User> Users { get; set; }
public List<MyModel> GetAllUsers()
{
var queryString = "SELECT Name FROM Users";
var adapter = new SqlDataAdapter(queryString, System.Configuration.ConfigurationManager.ConnectionStrings["SQLConn"].ConnectionString);
var current = new DataSet();
adapter.Fill(current, "Name");
return (from DataRow item in current.Tables[0].Rows
select new MyModel()
{
Name = Convert.ToString(item["Name"]),
}).ToList();
}
Controller
public ActionResult GetUser()
{
var model = new MyModel();
_db.GetAllUsers();
return View(model);
}
View
#model ModelName.Models.MyModel
--HTMLCode--
#foreach (var item in Model.Users) <-- Exception.
{
<tr>
<td>#Html.DisplayFor(modelItem => item.Name)</td>
</tr>
}
What am I forgetting?
Your problem is that you are getting the users, but you are not passing the users to the view, instead, you pass an empty model.
A better approach would be to create a ViewModel with all the properties you need in the view.
Let's say your view model looks something like this:
ViewModel:
class YourViewModel
{
public string Name { get; set; }
public bool TriggerOnLoad { get; set; }
public IEnumerable<User> Users { get; set; }
}
After creating your ViewModel what you need to do is that you need to create an instance of the ViewModel in your Controller and fill it with data and then pass the ViewModel to the View. In your case it would look something like this:
Action:
public ActionResult RandomAction()
{
YourViewModel vm = new YourViewModel()
{
Users = _db.GetAllUsers(),
Name = "Random Name",
TriggerOnLoad = true
};
return View(vm);
}
Later on, if you decide you need some extra properties you need to work with in your view, you just add them to your ViewModel and continue on.
Pass users like this. You are not passing the users to View
public ActionResult GetUser()
{
var model = new MyModel();
var users = _db.GetAllUsers();
return View(users);
}
Then in View
#model List<MyModel>
#foreach (var item in Model) //foreach through the list
{
<tr>
<td>#Html.DisplayFor(model => item.Name)</td>
</tr>
}

Querying database but getting null back

I'm trying to list the items from my database into my view but I'm getting null back.
I know the connection must be working to a certain extent because in my database the tables didn't exist but once I ran my program it did create the tables. However when I add content into my table my view still returns NULL.
Also, haven't touched the Review table yet, just worried about getting Restaurants working.
Restaurant.cs
namespace OdeToFood.Models
{
public class Restaurant
{
public int Id { get; set; }
public string Name { get; set; }
public string City { get; set; }
public string Country { get; set; }
public ICollection<RestaurantReview> Reviews { get; set; }
}
}
OdeToFood.cs
namespace OdeToFood.Models
{
public class OdeToFoodDb : DbContext
{
public DbSet<Restaurant> Restaurants { get; set; }
public DbSet<RestaurantReview> Reviews { get; set; }
}
}
Controller
OdeToFoodDb _db = new OdeToFoodDb();
public ActionResult Index()
{
var model = _db.Restaurants.ToList();
return View();
}
Index.cshtml
#model IEnumerable<OdeToFood.Models.Restaurant>
#{
ViewBag.Title = "Home Page";
}
#{
if (Model != null)
{
foreach (var item in Model)
{
<div>
<h4>#item.Name</h4>
<div>#item.City, #item.Country</div>
<hr />
</div>
}
}
else
{
<h1>Null</h1>
}
}
You need to pass to model back to the view.
OdeToFoodDb _db = new OdeToFoodDb();
public ActionResult Index()
{
var model = _db.Restaurants.ToList();
return View(model);
}
You never actually send the model to the view. Pass it as an argument:
OdeToFoodDb _db = new OdeToFoodDb();
public ActionResult Index()
{
var model = _db.Restaurants.ToList();
return View(model);
}
Additionally, it's generally a good idea not to create database contexts in a shared scope. Keep the context as close to where it's used as possible and only expand its scope when you really need to. Something like this:
public ActionResult Index()
{
using (var _db = new OdeToFoodDb())
{
var model = _db.Restaurants.ToList();
return View(model);
}
}
Database contexts/connections in a shared scope is just asking for problems unless you pay close attention to what you're doing. As the code gets more complex, it becomes more likely that other methods will try to use it and it may be in an unknown state at that time.

Entity framework how to pass data to view?

I am learning Entity Framework and MVC.
This is my model:
public class ChatLogContext : DbContext
{
public ChatLogContext()
: base("connString")
{
}
public DbSet<ChatLogs> ChatLogs { get; set; }
}
[Table("ChatLogs")]
public class ChatLogs
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ChatLogId { get; set; }
[Column("Message")]
public string Message { get; set; }
[Column("UserId")]
public int UserId { get; set; }
}
And this is my controller code here:
public ActionResult Index()
{
using(var db = new ChatLogContext())
{
var list = db.ChatLogs.Select(p => p.Message).SingleOrDefault();
ViewBag.data = list;
return View();
}
}
I then access that data in view like this:
#model Chat.Models.ChatLogs
#Html.Raw(ViewBag.data)
I can access 1 record as seen here with this.
But I would like to learn, how to access all records from the table ChatLogs with Entity Framework and pass it to view with Razor method(foreach), so I can format that data (I don't like default tables that VS generates). I am now using ViewBag for one row and 1 column, this is the most far I came.
I just can't find an examples on Google that would help my brains.
Help appreciated.
PS: Is it better to work with pure entity or mix linq(linq to entities)?
Typically the Index action is for showing a grid of all the entities (in this case ChatLogs).
One of the points of the Razor View Engine is that you get typed Views. So typically I would pass the data to the view directly as opposed to using the ViewBag.
public ActionResult Index()
{
using(var db = new ChatLogContext())
{
var list = db.ChatLogs.ToList();
return View(list);
}
}
The next step is to have the View typed to IEnumerable<ChatLog>. Visual Studio should help you with that. Then you can just foreach over the ChatLogs.

How to use SelectList in View without a model?

I have read many times experts of MVC saying that if I were to use a SelectList, it's best to have a IEnumerable<SelectList> defined in my model.
For example, in this question.
Consider this simple example:
public class Car()
{
public string MyBrand { get; set; }
public IEnumerable<SelectListItem> CarBrands { get; set; } // Sorry, mistyped, it shoudl be SelectListItem rather than CarBrand
}
In Controller, people would do:
public ActionResult Index()
{
var c = new Car
{
CarBrands = new List<CarBrand>
{
// And here goes all the options..
}
}
return View(c);
}
However, from Pro ASP.NET MVC, I learned this way of Creating a new instance.
public ActionResult Create() // Get
{
return View()
}
[HttpPost]
public ActionResult Create(Car c)
{
if(ModelState.IsValid) // Then add it to database
}
My question is: How should I pass the SelectList to View? Since in the Get method there is no model existing, there seems to be no way that I could do this.
I could certainly do it using ViewBag, but I was told to avoid using ViewBag as it causes problems. I'm wondering what are my options.
You could create a ViewModel that has all the properties of Car which you want on your form then make your SelectList a property of that ViewModel class
public class AddCarViewModel
{
public int CarName { get; set; }
public string CarModel { get; set; }
... etc
public SelectList MyList
{
get;
set;
}
}
Your controller will look like
public ActionResult Create() // Get
{
AddCarViewModel model = new AddCarViewModel();
return View(model)
}
[HttpPost]
public ActionResult Create(AddCarViewModel c)
{
if(ModelState.IsValid) // Then add it to database
}
MarkUp
#Html.DropDownListFor(#model => model.ListProperty, Model.MyList, ....)
Easy way, this is a copy of my code
without model
In the controller
ViewBag.poste_id = new SelectList(db.Postes, "Id", "designation");
In the view
#Html.DropDownList("poste_id", null," -- ", htmlAttributes: new { #class = "form-control" })

ASP MVC many-to-many relationship - retrieve data from associated table

I am trying to create a database with a many-to-many relationship using EF code first.
public class Item
{
public int ItemId { get; set; }
public String Description { get; set; }
public ICollection<Tag> Tags { get; set; }
public Item()
{
Tags = new HashSet<Tag>();
}
}
public class Tag
{
public int TagId { get; set; }
public String Text { get; set; }
public ICollection<Item> Presentations { get; set; }
public Tag()
{
Presentations = new HashSet<Item>();
}
}
public class ItemsEntities : DbContext
{
public DbSet<Item> Items { get; set; }
public DbSet<Tag> Tags { get; set; }
}
After that I'm adding an Item to the database
var tag = new Tag { Text = "tag1" };
var item = new Item
{
Description = "description1",
Tags = new List<Tag>()
};
item.Tags.Add(tag);
using (var db = new ItemsEntities())
{
db.Items.Add(item);
db.SaveChanges();
}
The problem is that I can't output items with their associated tags. The controller looks like this:
public ActionResult Index()
{
ItemsEntities db = new ItemsEntities();
return View(db.Items.ToList());
}
and the view page has the following code:
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(model => item.Description)
</td>
<td>
#foreach (var tag in item.Tags)
{
#tag.Text
}
</td>
</tr>
}
I expect the table to contain "description1" and "tag1" but I get only "description1". I really don't understand where the problem is. What is the correct way to do this?
Your navigation properties need to be marked virtual.
public class Item
{
public int ItemId { get; set; }
public String Description { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
public Item()
{
Tags = new HashSet<Tag>();
}
}
public class Tag
{
public int TagId { get; set; }
public String Text { get; set; }
public virtual ICollection<Item> Presentations { get; set; }
public Tag()
{
Presentations = new HashSet<Item>();
}
}
To make your code work, you could mark your collection properties as virtual stated by #danludwig. By marking the collection properties as virtual EF Code First will lazy load those properties when iterating over the items in your view. You run into a SELECT N+1 problem using this approach. Let's examine your view code:
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(model => item.Description)
</td>
<td>
#foreach (var tag in item.Tags)
{
#tag.Text
}
</td>
</tr>
}
In this foreach loop you iterate over all items in your Model that were selected using the EF data context.
db.Items.ToList()
This is your first select. But in your view above every time you access an item's Tags property another select is executed. The important thing is FOR EVERY ITEM. That means if you have 100 Items in db.Items DbSet, you'll execute 101 selects. This is not acceptable for most systems.
A better approach is to pre select the tags for each item. One approach is to use Include or to select the tags related to an item into dedicated object.
public class ItemWithTags
{
public Item Item { get;set; }
public IEnumerable<Tag> Tags { get;set; }
}
public ActionResult Index()
{
ItemsEntities db = new ItemsEntities();
var itemsWithTags = db.Items.Select(item => new ItemWithTags() { Item = item, Tags = item.Tags});
return View(itemsWithTags.ToList());
}
In your view you can iterate over the itemsWithTags collection, access items's properties and for tags you access the Tags property of ItemWithTags.
Another problem with your code is, that the ItemsEntities DbContext is opened in your code but never closed. You can use the VS MVC Templates to generate a Controller that handles DbContext opening and closing correctly!
You can use a tool like MVC Mini Profiler for to inspect the commands executed against the database. This Stackoverflow Question shows how to set up MVC Mini Profiler with EF Code First.

Resources