Translate SQL to LINQ and how display new results into the view? - c#

I have to questions:
1) How to translate this select to LINQ?
Select i.ID, i.Impression,
(Select COUNT(ImpressionsId)
from DiaryImpressions
where DiaryPostsId = '2' AND ImpressionsId = i.ID) as Num from Impressions i
2) I have to run this query inside a loop where I could replace the hard coded value 2 for the real post ID. But how could I do this in C# with MVC3? Could someone give me any example on how it works inside foreach loop and how could I display this value in the view?
Thanx a lot =)

Have you already got a database context/repository set up to communicate with the database in your MVC project? Below I'm assuming you've got a database context named dbContext.
Note: If you're not using Razor, replace the "#" with "<%" and close with "%>"
You can either put the item in the ViewBag, or in the Model (I prefer Model) like below. I'm not sure what you want to do with these counts, so i've put them in a list. You'll need a class to put your impression info in, i've added it below;
Model Code:
public class MyModel
{
public class ImpressionInfo //just used to store your results sub class of the model
{
public ImpressionInfo(id, impression, diaryImpressionCount)
{
Id = id;
Impression = impression;
DiaryImpressionCount = diaryImpressionCount
}
public int Id { get; set; }
public int Impression { get; set; } //is this an int? you didn't say
public int DiaryImpressionCount { get; set; }
}
public MyModel()
{
var impressionInfo = new List<ImpressionInfo>()
foreach (var di in dbContext.DiaryImpressions)
{
ImpressionInfos.Add(new ImpressionInfo(
di.Id,
di.Impression,
dbContext.DiaryPosts
.Count(dp => dp.ImpressionsId == di.ID));
}
}
public List<ImpressionInfo> ImpressionInfos { get; set; }
then in the view
View Code:
#model MyModel
#if(Model.ImpressionInfos.Count > 0)
{
<table>
<tr>
<td>Impression</td>
<td>Count</td>
</tr>
foreach (var i in Model.ImpressionInfos)
{
<tr>
<td>#i.Impression</td>
<td>#i.DiaryImpressionCount</td>
</tr>
}
}
else
{
<p>No Impression infos</p>
}
Hope this helps.

Related

Using #Html.TextBoxFor with ViewModel

A quick question:
So, I am developing a small MVC/C# application and I am using ViewModel to pass data to my view. The ViewModel is actually a combination of 3 Models.
My Models:
public class Sourcing_ProfileSourcingAreas
{
public string Area { get; set; }
public string Details { get; set; }
}
public class DefCountry
{
public string CountryName { get; set; }
public string CountryCode { get; set; }
}
My ViewModel:
public class SourcingIndex
{
public List<DefCountry> CountryLst { get; set; }
public List<Sourcing_ProfileSourcingAreas> AreaLst { get; set; }
}
On my view, I put this line at the top #model SourcingIndex to declare that I will be using this ViewModel
I also was easily able to specify which model I want to display by using foreach loop, for example:
#foreach (Sourcing_ProfileSourcingAreas area in Model.AreaLst)
{
<tr>
<td>#area.Area</td>
<td>#area.Details</td>
</tr>
}
And
#foreach (DefCountry ctry in Model.CountryLst)
{
<option>#ctry.CountryName</option>
}
However, I am not able to create #Html.TextBox and assign it to a specific property in a model!
If possible, I want it to be somthing like this: #Html.TextBoxFor(model => model.AreaLst.Area)
Thank you
It's a bit of a weird quirk about Razor. If you try and access the objects in a foreach loop it struggles to resolve where in the model it is. You need to use this syntax:
#for (int x = 0; x < Model.CountryLst.Count(); x++)
{
#Html.TextBoxFor(t => t.CountryLst[x].CountryName)
}
this should produce an input something like
<input name="countryLst[1].CountryName"/>
#for(var i = 0; i < Model.CountryLst.Count(); i++)
{
<text>#Html.TextBoxFor(p=>Model.CountryLst[i].CountryCode)</text>
}

My blog only retrieves one post

I'm creating a blog in C# MVC, but I'm having a problem with the blog posts. From a list of posts, only one post is retrieved, and it's the last one. I wanna retrieve all blog posts with their associated images...
BLOG MODELS
public class Blog
{
[Key]
public int BlogID { get; set; }
public string Title { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
public class Post
{
[Key]
public int PostID { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogID { get; set; }
public bool hasImages { get; set; }
public virtual Blog Blog { get; set; }
public virtual ICollection<ImageFile> ImageFiles { get; set; }
}
public class ImageFile
{
[Key]
public int ImageID { get; set; }
public string ImageName { get; set; }
public string ImageType { get; set; }
public byte[] ImageBytes { get; set; }
public int PostID { get; set; }
public virtual Post Post { get; set; }
}
METHOD TO RETRIEVE POSTS + IMAGES
public Tuple<IEnumerable<Post>, IEnumerable<ImageFile>> getAllBlogData()
{
var AllPosts = _entities.Posts.ToList();
Tuple<IEnumerable<Post>, IEnumerable<ImageFile>> model = null;
foreach (var post in AllPosts)
{
var posts = AllPosts.Where(e => e.PostID == post.PostID);
if (posts != null)
{
//checks if a blog post has images
if (post.hasImages)
{
var images = _entities.ImageFiles.Where(e => e.PostID == post.PostID);
model = new Tuple<IEnumerable<Post>, IEnumerable<ImageFile>>(posts, images);
}
else
{
model = new Tuple<IEnumerable<Post>, IEnumerable<ImageFile>>(posts, null);
}
}
}
return model;
}
CONTROLLER
public ActionResult Posts()
{
return View(_blogrepository.getAllBlogData());
}
VIEW
#model Tuple<IEnumerable<Winter.Domain.Entities.Post>, IEnumerable<Winter.Domain.Entities.ImageFile>>
#{
ViewBag.Title = "Posts";
}
<hgroup>
<h3>BLOG</h3>
</hgroup>
#foreach (var post in Model.Item1)
{
<p class="container images">
#if (Model.Item2 != null)
{
foreach (var image in Model.Item2)
{
<span>
<img src="data:image/png;base64,#Convert.ToBase64String(image.ImageBytes)" />
</span>
}
<div class="clearfix"></div>
}
<hgroup>
<h2>#Html.DisplayFor(modelItem => post.Title)</h2>
</hgroup>
<span>
#Html.DisplayFor(modelItem => post.Content)
</span>
</p>
}
<hr />
Thanks in advance
Below, I give you 2 ways of making your code work.
The first being the "correct" way to implement what you're trying to do and the second being a fix of the current code you're using.
Get a beverage, and have a good read. note: this is all off the top of my head (so please forgive in advance - just comment anything that needs correcting)
1) The "correct" way to do it:
It looks like you're using Entity Framework. (If not, then why not?)
That said, I would look into Entity Framework Relationships & Navigation Properties.
This is exactly what Database Relationships are for. Your code is currently making heavy work of what is an in-built feature, and you're using Tuples unnecessarily. EF is there to allow you to use real objects you've already created.
Here's a short(ish) way you should update your structure (this example is using EF Code-First - I find it way easier to create the DB from already-made classes that I use around the program):
Classes
public class Post
{
public int Id { get;set; }
public string Title { get; set; }
// Other properties etc.
public virtual ICollection<ImageFile> Images { get; set; }
}
public class ImageFile
{
public int Id { get; set; }
public byte[] ImageBytes { get; set; }
[ForeignKey(nameof(Post))]
public int PostId { get; set; }
[ForeignKey(nameof(PostId))]
public Post Post { get; set; }
}
This gives you a one-to-many relationship of "one post" has "many image files". The virtual property of Images in the Post class means it is a "navigation property" - this means that EF can figure out the relationship between the ImageFile that have the matching PostID to that post. This is down to FluentAPI's convention (TL;DR)
See how Code First Relationships work in Entity Framework for more information.
Controller
Your controller can then return a List<Post> from your context.
public ActionResult Posts()
{
var posts = _yourDbContext.Posts.Include(x => x.Images).ToList();
return View(posts);
}
Note: You will need to include using System.Data.Entity at the top of your file's using statements, in order to use the Include() method with a lambda expression.
Further note: If you don't "flatten" the results to a List<>, then you might not (off the top of my head) need to use the Include() method - TL;DR: when the page is rendered, it will still have the context in scope, and will be able access the navigation properties using the query (IQueryable<>) it still uses (lazy loading). Include() simply uses eager loading to pull the images out and place them in the List<>.
View
Then, in your view you can simply use the #model as an IEnumerable<Post>, iterating over the List/IEnumerable you passed in; referencing the Images property that way.
Like so:
#model IEnumerable<Your.Namespace.Post>
// Other fluff - page title etc.
#foreach (var post in Model)
{
foreach (var image in post.Images)
{
// Display each image - #image.ImageBytes
}
<h1>#post.Title</h1>
// Other #post.Property printing, here
}
Using this way will enable you to (use a lot less code), and use your classes for what they would usually be designed for in OOP.
I have also included a "dirty" answer - using the current method you're using, below:
2) The "dirty" fix of your code:
Similar to #Jauch's answer, but a little more condensed for both the code, and your view:
Your getAllBlogPosts() method is simply returning the last post in the iteration, every time - you're overwriting model each time.
You could simply return a list of blog posts like this:
private List<Tuple<Post, IEnumerable<ImageFile>>> GetAllBlogPosts()
{
var allPosts =
_entities.Posts.Select(
post =>
new Tuple<Post, IEnumerable<ImageFile>>(
post,
_entities.ImageFiles.Where(x => x.PostID == post.PostID)));
return allPosts.ToList();
}
Then, in your view you can simply iterate the .Item2 property (the images), without having to do a null check beforehand.
This is because, even if there are no results, the IEnumerable<ImageFile> will result as simply an empty collection.
In the view, your model will be an IEnumerable<Tuple<Post, IEnumerable<ImageFile>>> ( whereFQNS is the fully qualified namespace of your classes):
#model IEnumerable<Tuple<FQNS.Post, IEnumerable<FQNS.ImageFile>>>
Your view can then iterate over each Tuple in the Model - let's say item.
Then inside the item, you can reference Value1 (being the Post) and Value2 (being the Post's images).
Like so:
#foreach (var item in Model)
{
<p class="container images">
foreach (var image in item.Item2)
{
<span>
<img src="data:image/png;base64,#Convert.ToBase64String(image.ImageBytes)" />
</span>
}
<div class="clearfix"></div>
<hgroup>
<h2>#Html.DisplayFor(x => item.Value1.Title)</h2>
</hgroup>
<span>
#Html.DisplayFor(x => item.Value1.Content)
</span>
</p>
}
I hope that this helps you along the way with your project, whichever method you take.
I would heavily advise to research into EF's relational capabilities and using navigation properties in order to make your code much cleaner (and your life much easier).
Hope this helps! :)
I would say that the problem is in this part of the code:
public Tuple<IEnumerable<Post>, IEnumerable<ImageFile>> getAllBlogData()
{
var AllPosts = _entities.Posts.ToList();
Tuple<IEnumerable<Post>, IEnumerable<ImageFile>> model = null;
foreach (var post in AllPosts)
{
var posts = AllPosts.Where(e => e.PostID == post.PostID);
if (posts != null)
{
//checks if a blog post has images
if (post.hasImages)
{
var images = _entities.ImageFiles.Where(e => e.PostID == post.PostID);
model = new Tuple<IEnumerable<Post>, IEnumerable<ImageFile>>(posts, images);
}
else
{
model = new Tuple<IEnumerable<Post>, IEnumerable<ImageFile>>(posts, null);
}
}
}
return model;
}
First, you load all posts:
var AllPosts = _entities.Posts.ToList();
Then you iterate on each post on AllPosts, and look for all posts that have the same ID than this one:
foreach (var post in AllPosts)
{
var posts = AllPosts.Where(e => e.PostID == post.PostID);
Assuming that PostID is a "unique" KEY (like you showed in the code), you will find just the post itself.
Than you create the "model". And do this for each post. At the end, "model" will contain only the last post from the first foreach.
I think you need to return a "list" of posts (and associated images), so, I would try to change the code in this way:
public List<Tuple<Post, IEnumerable<ImageFile>>> getAllBlogData()
{
var list = new List<Tuple<Post, IEnumerable<ImageFile>>();
Tuple<Post, IEnumerable<ImageFile>> tuple = null;
var AllPosts = _entities.Posts.ToList();
foreach (var post in AllPosts)
{
//checks if a blog post has images
if (post.hasImages)
{
var images = _entities.ImageFiles.Where(e => e.PostID == post.PostID);
tuple = new Tuple<Post, IEnumerable<ImageFile>>(post, images);
}
else
{
tuple = new Tuple<Post, IEnumerable<ImageFile>>(post, null);
}
list.Add(tuple);
}
return list;
}
And, of course, adapt you file to deal with the list instead of the "model".
The code above should return all the posts in your blog, through a list that has tuples where on data is the POST, and the other is the list of images associated with that post.

MVC 5: ViewModel / Passing lists for the create

So I am currently studying and analyzing the use of ViewModels.
In my Application (a so called "Restaurant") I want the ability for my "users" to create a menu.
When they want to create a menu: They can choose the name + the amount of persons that can join the menu. BUT also, they can add an amount of dishes that are already in the restaurant. This will be in the style of checkboxes and an 'Create'-Button at the end.
This means I had to use a ViewModel. I am currently trying to give the possibility to add a list of dishes to a menu for the creation. But I'm stuck at the for loop, used to loop through the dishes. Or better, I'm stuck at the whole concept:
What is the best way to display all the already created dishes to the CreateMenu View? Is it still possible to loop through a ViewBag if I will add them in a ViewBag?
Lets say I successfully tried to do what I wanted to do. How would I create a new Menu based (or extracted?) from the ViewModel?
In my Code, please note that the Menu - Model cannot be changed really because I already use a list of Dishes from it (In another view, where I display all the menu's and their dishes).
also ignore the possibility of wrong names or spelling mistakes in data, since I translated everything from Flemish
Models
public class Menu
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Range(0,10)]
public int AmountPersons { get; set; }
[Range(0,double.MaxValue)]
public double Price { get; set; }
public virtual List<Dish> Dishes { get; set; }
}
public class Dish
{
[Required]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public enum Types { VOORGERECHT, HOOFDGERECHT, DRANK, DESSERT}
public Types Type { get; set; }
public double Price { get; set; }
public virtual List<Menu> Menus { get; set; }
public virtual List<Table> Tables { get; set; }
//Checked used for the 'checkbox' in the CreateMenu-View
[NotMapped]
public bool Checked { get; set; }
}
public class MenuViewModel
{
public Menu Menu { get; set; }
public List<Dish> AddedDishes { get; set; }
}
Controller
public ActionResult CreateMenu( )
{
MenuViewModel gm = new MenuViewModel();
// Assign ALL already created dishes to the list that the user can choose.
// AddedDishes is wrong? ViewBag preferred?
gm.AddedDishes = db.Dishes.ToList();
return View(gm);
}
// Add the Menu to all the Menu's in the Database.
[HttpPost]
public ActionResult MenuAanmaken(MenuModel gm)
{
// code to save the menu with all the added dishes to the database
// Conflict!? Cannot convert the MenuViewModel to the Menu-model How do we need to extract the Menu and the AddedDishes list
// to a menu and save that one to the database?
db.Menus.Add(gm);
return View(gm);
}
View
#using VBExamen.Models
#model MenuViewModel
....
#Html.LabelFor(m => m.Menu.Name)
#Html.EditorFor(m => m.Menu.Name)
#Html.LabelFor(m => m.Menu.AmountPersons)
#Html.EditorFor(m => m.Menu.AmountPersons)
#for(int i = 0; i < Model.AddedDishes.Count; i++)
{
<tr>
<td>
#Html.DisplayFor( .Name)
#Html.HiddenFor(item => .Id)
#Html.CheckBoxFor(item => .Checked)
</td>
</tr>
}
E D I T E D _ U P D A T E (SEE BELOW)
Okay So I think I'm close now,
I edited my classes as the following:
public class MenuViewModel<T>
{
public Menu Menu { get; set; }
public List<T> DishList { get; set; }
public MenuViewModel()
{
this.Lijst = new List<T>();
}
}
Controller
public ActionResult CreateMenu(MenuViewModel<Dish> model )
{
model.DishList = db.Gerechten.ToList();
return View(model);
}
[HttpPost]
public ActionResult CreateMenu(MenuViewModel<Dish> model,List<Gerecht> SelectedList)
{
Menu t = new Menu();
t.Naam = gm.Menu.Naam;
t.AmountPersons = gm.Menu.AmountPersons;
t.Dishes = SelectedList;
db.Menus.Add(t);
return View("Menus", model);
}
View function creating list
#for (int i = 0; i < Model.DishList.Count(); i++)
{
<tr>
<td>
#Html.Label(Model.DishList[i].Naam)
<input type="hidden" name=#String.Format("DishList[{0}].Id", i) value=#Model.DishList.ElementAt(i).Id />
<input type="hidden" name=#String.Format("DishList[{0}].Name", i) value=#Model.DishList.ElementAt(i).Name />
<input type="checkbox" name=#String.Format("DishList[{0}].value", i) />
<input type="hidden" name=#String.Format("DishList[{0}].value", i) value="false" />
</td>
<br />
</tr>
}
I did this after watching about 10 tutorials about ViewModels, is my next approach better than the first one?
I think so because i get the following on my screen:
I was thinking what the next approach would be. I was thinking about comparing the 2 lists (1 of the viewmodel, 1 passed) and see the checkbox statuses?
UPDATE
After Stephen Muecke's answer I re-edited my code but found a problem that I can't seem to understand.
The answer says I should be in the position of a 1-to-many table in the form as a class:
// You have not indicated the 1-many table the dishes are saved to so adjust as required
MenuDish dish = new MenuDish()
{
MenuId = menu.ID,
DishId = dish
};
db.MenuDishes.Add(dish);
However, what we've learned at school was, that if you create lists in the data-models of the entities, linked tables will be automatically generated in the Database. And that is exactly what my DB has done (without the creation of the MenuDish class):
MenuGerechts stands for MenuDish.
This is the automatically created table done by the entity framework.
That brings me to the following questions. I have re-edited the controller to the following:
[HttpPost]
public ActionResult MenuAanmaken(MenuVM model)
{
if (!ModelState.IsValid)
{
return View(model);
}
IEnumerable<int> selectedDishes = model.Dishes.Where(x => x.IsSelected).Select(x => x.ID);
Menu menu = new Menu()
{
Naam = model.Name,
AantalPersonen = model.AmountPersons
};
foreach (int id in selectedDishes)
{
Dish g = db.Dishes.Find(id);
menu.Dishes.Add(g);
};
db.Menus.Add(menu);
db.SaveChanges();
return RedirectToAction("Menus", "Menu");
}
I get the Object reference not set to an instance of an object error and I'm understanding why ofcourse.
I have done the changes since the Data-Model Menu, already has a List of Dishes. But assuming the answer of S. Muecke, this isn't the correct way to solve this ViewModel since he proposes the use of a New Class (that is created to support the one-to-many relationship)?
This brings me to the conclusion of the following questions:
Why is it impossible or not-recommended to directly add the selected dishes to the menu instance?
Is it always needed to create the in between table 'MenuDish' in a Data-model?
Will the following code still work (showing the menu's and their dishes) after creating new Menu's?:
Controller:
public ActionResult Menus()
{
List<Menu> menus = db.Menus.ToList();
return View(menus);
}
View:
#model IEnumerable<VBExamen.Models.Menu>
#{
ViewBag.Title = "Menus";
}
<h2>Menus</h2>
<p>
#Html.ActionLink("Create New Menu", "CreateMenu")
</p>
#foreach (var item in Model)
{
<table>
<ul>
<p>#Html.DisplayFor(modelItem => item.Name)</p>
#foreach (var g in item.Dishes)
{
<li>
#Html.DisplayFor(modelItem => g.Name)
</li>
}
</ul>
</table>
}
Which outputs the following:
What would be good motivations to do this?
UPDATE 2
So I have included the following in my project:
** I have used the Table()- annotation to make it use the one that's already created**
**Model: **
[Table("MenuGerechts")]
public class MenuGerechts
{
[Key]
[ForeignKey("Menu")]
public virtual int? MenuId { get; set; }
public virtual Menu Menu { get; set; }
[ForeignKey("Dish")]
public virtual int? DishId { get; set; }
public virtual Dish Dish { get; set; }
}
I have then actually created new menus successfully! But when I go to the overview menu page (from the pic above), it only shows the Name of the menu, and not the list of meals that it includes.
The Database however didn't allow my MenuDish link table to be used for my newly created class (it created a new one, and renamed the old one with the 'old' menus with a '1' behind it:
Hence why I was asking my previous questions. Does this mean my whole approach to this exercise was wrong?
New Question:
My menuCreate ViewModel only works if i Select 1 dish? Why is this so? I get the following error The role 'MenuGerechts_Menu_Source' of the relationship 'VBExamen.Models.MenuGerechts_Menu' has multiplicity 1 or 0..1.
Firstly a view model should not contain properties which are data models. It should contains only properties which you display/edit in the view, and I recommend you read What is ViewModel in MVC?.
Based in the image of the form you have shown, your view models needs to be (display and validation attributes omitted for simplicity)
public class MenuVM
{
public int? ID { get; set; } // included so this can be used for editing as well as creating
public string Name { get; set; }
public int AmountPersons { get; set; }
public List<DishVM> Dishes { get; set; }
}
public class DishVM
{
public int ID { get; set; }
public string Name { get; set; }
public bool IsSelected { get; set; }
}
and the controller GET method
public ActionResult CreateMenu( )
{
// Get all the dishes from the database
var dishes = db.Dishes; // modify to suit
// Initialize the view model
var model = new MenuVM()
{
Dishes = dishes.Select(x => new DishVM()
{
ID = x.Id,
Name = x.Name
}).ToList()
};
return View(model);
}
Then in the view (LabelFor() and ValidationFor() methods omitted for simplicity)
#model MenuVM
#using (Html.BeginForm())
{
#Html.TextBoxFor(m => m.Name)
#Html.TextBoxFor(m => m.AmountPersons )
for(int i = 0; i < Model.Dishes.Count; i++)
{
<div>
#Html.HiddenFor(m => m.Dishes[i].ID)
#Html.HiddenFor(m => m.Dishes[i].Name)
#Html.CheckBoxFor(m => m.Dishes[i].IsSelected)
#Html.LabelFor(m => m.Dishes[i].IsSelected, Model.Dishes[i].Name)
</div>
}
<input type="submit" value="Create" />
}
And finally the POST method will be
public ActionResult CreateMenu(MenuVM model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// Initialize and save the Menu
Menu menu = new Menu()
{
Name = model.Name,
AmountPersons = model.AmountPersons
};
db.Menus.Add(menu);
db.SaveChanges(); // you now have the ID of the new menu
// Save the dishes associated with the menu
IEnumerable<int> selectedDishes = model.Dishes.Where(x => x.IsSelected).Select(x => x.ID);
foreach(int id in selectedDishes)
{
// You have not indicated the 1-many table the dishes are saved to so adjust as required
MenuDish dish = new MenuDish()
{
MenuId = menu.ID,
DishId = dish
};
db.MenuDishes.Add(dish);
}
db.SaveChanges(); // save the selected dishes
return RedirectToAction(...); // redirect somewhere
}
Side note: Remove the [NotMapped] public bool Checked { get; set; } property from your data model.
This is only the answer to your first question... I gotta get back to work :)
I strongly advise you to use Entity Framework for storing this information, as creating the data context, Initializer(Entity Framework Requirements) and View Model will allow you to scaffold everything in your project including controllers and views. This means you take the information from the ViewModel class rather than from the view bag.
Scaffolding means that Visual Studio will create all your code to CRUD(Create, Read, Update, Delete) Controllers and Views to allow this to happen. Freeing you from either 45 mins of furious typing or hours of banging your head against a wall.
So lets do this, First we create our own context class inheriting from DbContext (A part of Entity Framework):
public class MenuContext : DbContext {
public MenuContext() : base("MenuContext") {
}
The base referenced here specifies the name in your web.config file of your connection string which we will set up momentarily. Alternatively you can specify your connection string in place of the name.
public DbSet<MenuViewModel> Menus { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
The Initializer class we will set up next populates the database if the database does not already exist at the connection string location.
class MenuInitializer : CreateDatabaseIfNotExists<MenuContext> {
protected override void Seed(MenuContext context) {
// This sets up your database to populate with whatever you type into this method.
}
}
Now you are able to go to your solution explorer, right click on your controllers folder and click add - Controller. Then specify that you want MVC 5 Controller with views, using Entity Framework. Click - Add.
A dialog will show, specify your view model, the context class we set up, make sure "Generate Views" is selected, name your controller and BAM! Build your project and view your auto created everything!

Cannot get my ViewModel to work in MVC ASP.NET

I am having alot of difficult getting my viewmodel to work correctly. As a bit of context i am using a viewmodel to use two models from another project. These models contain the User information and thier chosen Device in a localdb. However i cannot currently use a view to display the data from both of those models on one view so i created a viewmodel.
However I am current recieving:
Error: 'System.Collections.Generic.IEnumerable' does not contain a definition for 'UserID' and no extension method 'UserID' accepting a first argument of type 'System.Collections.Generic.IEnumerable' could be found (are you missing a using directive or an assembly reference?)
This error is occurring for all of the model objects. If i can get around it it will be the first step to a functioning view model. Any help would be greatly appreciated.
User.cs - Model (in project: FaceToFace)
namespace FaceToFace.Model
{
public class User
{
public int UserID { get; set; }
public string CodeName { get; set; }
public bool UseBriefInstructions { get; set; }
public ICollection<RegimeItem> RegimeItems { get; set; }
public Device Device { get; set; }
public virtual ICollection<Grading> UserGradings { get; set; }
public User()
{
this.RegimeItems = new List<RegimeItem>();
Device = new Device();
}
}
public class RegimeItem
{
public int RegimeItemID { get; set; }
public Exercise RegimeExercise { get; set; }
}
}
Device.cs - Model (in project: FaceToFace)
namespace FaceToFace.Model
{
public class Device
{
public int DeviceID { get; set; }
public string Name { get; set; }
}
}
UserDeviceViewModel.cs (in project: FaceToFaceWebsite)
namespace FaceToFaceWebsite.Models
{
public class UserDeviceViewModel
{
public UserDeviceViewModel()
{
User = new User();
Devices = new List<SelectListItem>();
}
public User User { get; set; }
public IList<SelectListItem> Devices { get; set; }
}
}
PatientController.cs - Only a segment of the entire page to avoid spam (Project: FaceToFaceWebsite)
namespace FaceToFaceWebsite.Controllers
{
public class PatientController : Controller
{
private F2FData db = new F2FData();
public ActionResult Index()
{
var viewModel = new List<FaceToFaceWebsite.Models.UserDeviceViewModel>();
return View(viewModel);
}
}
}
Views/Patient/Index.cshtml (facetofacewebsite)
#model IEnumerable<FaceToFaceWebsite.Models.UserDeviceViewModel>
#*#model FaceToFaceWebsite.Models.UserDeviceViewModel*#
#*#model IEnumerable<FaceToFace.Model.User>*#
<h2>Your Patients</h2>
#*Showing #Model.Count() users*#
<p>#Html.ActionLink("Add New User", "Create")</p>
<table>
<tr>
<th>#Html.DisplayNameFor(model => model.UserID)</th>
<th>#Html.DisplayNameFor(model => model.CodeName)</th>
<th>#*#Html.DisplayNameFor(model => model.Device.Name)*#Device</th>
<th>#Html.DisplayNameFor(model => model.DeviceID)</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>#Html.DisplayFor(modelItem => item.UserID)</td>
<td>#Html.DisplayFor(modelItem => item.CodeName)</td>
<td>#Html.DisplayFor(modelItem => item.Name)</td>
<td>#Html.DisplayFor(modelItem => item.DeviceID)</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.UserID }) |
#Html.ActionLink("Details", "Details", new { id = item.UserID }) |
#Html.ActionLink("Delete", "Delete", new { id = item.UserID })
</td>
</tr>
}
</table>
So what i REALLY need to know is that by using model properties from another project, what do i have to do differently. What am i currently doing wrong? what should i do so that the USER information and the DEVICE information can be show.
UPDATE
Thanks to Stephen Muecke, the solution to the issue of the index view not displaying the user db data was solved by changing the Action result in the index controller to:
public ActionResult Index()
{
var viewModel = db.Users.Select(u => new UserDeviceViewModel() { User = u, Device = u.Device }).ToList();
return View(viewModel);
}
UserDeviceViewModel contains a property named User not UserID (which is a property of User. Your loop needs to be
#foreach (var item in Model)
{
<tr>
<td>#Html.DisplayFor(m => item.User.UserID)</td>
<td>#Html.DisplayFor(m => item.User.CodeName)</td>
Note you table headers wont work in this case.
Note also you are not really using a true 'view model'. A view model contains only those properties which you need for display/edit in a view (not just for dumping other models). Based on the view code you have shown it should be something like
public class UserDeviceViewModel
{
public int UserID { get; set; }
public string CodeName { get; set; }
public int DeviceID { get; set; }
public IList<SelectListItem> Devices { get; set; }
}
Although you view contains a reference to property Name (not sure what this is - perhaps DeviceName?) and your view does not use Devices (have you omitted some of the view?)
Remove the Ienumerable!
#model FaceToFaceWebsite.Models.UserDeviceViewModel
Look in your controller:
public ActionResult Index()
{
var viewModel = new
FaceToFaceWebsite.Models.UserDeviceViewModel();
return View(viewModel);
}
You are passing List (IEnumerable<FaceToFaceWebsite.Models.UserDeviceViewModel>) , while your view code expected to be FaceToFaceWebsite.Models.UserDeviceViewModel
Well you could pass the Correct type of ViewModel to your View:
In your View you have:
#model IEnumerable<FaceToFaceWebsite.Models.UserDeviceViewModel>
And in Controller you have:
var viewModel = new FaceToFaceWebsite.Models.UserDeviceViewModel();
return View(viewModel);
Try passing a List of your ViewModel:
var viewModel = new List<FaceToFaceWebsite.Models.UserDeviceViewModel>();
return View(viewModel);
OR:
In your View change this:
#model IEnumerable<FaceToFaceWebsite.Models.UserDeviceViewModel>
To:
#model FaceToFaceWebsite.Models.UserDeviceViewModel
WHY are you getting that ERROR Message?
Because your ViewModel doesn't have UserId, CodeName etc.
BUT your User Class has UserId and CodeName
So In ViewModel you will access like this:
ViewModel.User.UserId and ViewModel.User.CodeName:
Like This:
<th>#Html.DisplayNameFor(model => model.User.UserID)</th>
<th>#Html.DisplayNameFor(model => model.User.CodeName)</th>
As per given code snippet, your View mapping and data model not sync up.
can you just follow below steps
clear all views.
first display only user level info
Verify user level info are you able to? next proceed further device level
put device level loop for your devices collection (make sure your Device collection model m not sure about your "SelectedListItem"

Dynamically binding and creating items with complex database structure to Create and Edit views using viewModel in MVC 3 with EF4

I had bad times trying to figure out how to deal with this. Any help wold be appreciated. Even a suggestion of a better structure that fits to my needs: Construct category items in a category having an specification of how they can be in a category item property list.
This is used to, among other things, dinamicaly generate forms for creating and editing items.
Long story short: I need to know if I'm doing it right or a better (maybe automated) way of deal with it without breaking the whole app.
.
.
I'm working with MySQL 5 in VWD Express 2010 in a Win7 64bit machine with all MySQL drivers intalled (ODBC and .NET specific provider, the last one is not compatible with ASP.Net 4). Other problem rised here, but can be target for a separate question: I'm writing all of my models, 'cause MySql isn't compatible with the Linq to SQL (I can imagine why, but not sure).
.
.
Returning to the real topic:
My models are:
Category - Them main entity, with a name property, a collection of CategoryItemProperty entities and a collection of Item entities;
CategoryItemProperty - An entity with a name and some other properties that dictate how the Items in this category may be (field size, mask, input restriction, etc);
Item - The entity whose properties are based on the category properties;
ItemProperty - The properties of the items (field size, mask, input restriction, etc)
The code is something around this:
public class Category
{
public int CategoryId { get; set }
public string Description { get; set }
//...
public virtual List<CategoryItemProperty> ItemProperties { get; set; }
}
public class CategoryItemProperty
{
public int CategoryItemPropertyId { get; set; }
public string Label { get; set; }
public string Name { get; set; }
public int Size { get; set; }
public int MaxLenght { get; set; }
//...
public virtual Category Category { get; set; }
}
public class Item
{
public int ItemId { get; set; }
public string Description { get; set; }
public int CategoryId { get; set; }
//...
public virtual Category Category { get; set }
public virtual List<ItemProperty> Properties { get; set; }
}
public class ItemProperty
{
public int ItemPropertyId { get; set; }
public int ItemId { get; set; }
public int CategoryItemPropertyId { get; set; }
public string Value { get; set; }
//...
public virtual Item Item { get; set; }
public virtual CategoryItemProperty CategoryItemProperty { get; set; }
}
.
.
The big problem here, with this approach, is to generate the form and deal with the data on the controller side to be saved to the database.
.
.
A more detailed example wold be: Generate a simple contact form:
We create a Category with some field specification:
var category = new Category() { Description = "Simple Contact Form" };
MyEntitySet.Categories.Add(category);
MyEntitySet.SaveChanges();
//...
var name = new CategoryItemProperty() { Label = "Name", Size = 50, MaxLength = 50 };
var message = new CategoryItemProperty() { Label = "Message", Size = 50, MaxLength = 255 };
category.ItemProperties.Add(name);
category.ItemProperties.Add(message);
MyEntitySet.Entry(category).State = EntityState.Modified;
MyEntitySet.SaveChanges();
.
.
What I have came up until now is to create a ViewModel to pass the category info and its item property collection to the Create and Edit views and doing a loop through the ItemProperties to generate the fields and working in the ItemController, receiving the FormCollection and generating the Item and its ItemPropertys objects and saving them to the database. But this process is terrible and painfull:
.
Items/Create View:
#model MyApp.Models.CategoryItemModelView
#Html.EditorFor(m => m.Description);
...
#foreach(var property in Model.ItemProperties)
{
<label>#property.Label</label>
<input type="text" name="#property.Name" size="#item.Size" maxlength="#item.MaxLength" />
}
In the Controller:
// GET: /Items/Create/5
public ActionResult Create(int id)
{
var categoryItemModelView = new CategoryItemModelView();
categoryItemModelView.Populate(id); // this method maps the category POCO to the ViewModel
return View(categoryItemModelView);
}
// POST: /Items/Create/5
[HttpPost]
public ActionResult Create(int id, FormCollection formData)
{
if (ModelState.IsValid)
{
var category = MyEntitySet.Categories.Find(id);
var item = new Item
{
CategoryId = id,
Description = formData["Description"],
// ...
Category = category
};
MyEntitySet.Items.Add(item);
foreach(var property in category.ItemProperties)
{
var itemProperty = new ItemProperty
{
ItemId = item.ItemId,
CategoryItemPropertyId = property.Id,
Value = form[property.Name],
// ...
Item = item,
CategoryItemProperty = property
};
MyEntitySet.ItemProperties.Add(itemProperty);
}
MyEntitySet.SaveChanges();
return RedirectToAction("Index");
}
// Here I don't know how to return the typed data to the user (the form returns empty)
var categoryItemModelView = new CategoryItemModelView(id);
categoryItemModelView.Populate(id); // this method maps the category POCO to the ViewModel
return View(categoryItemModelView);
}
.
.
My problem rises in building the Create and Edit actions and its respective views (see above how I'm doing it right now). How to handle this case, when I have to use the Category.ItemProperties to generate the fields and store the information in an Item object and the field values in its ItemProperty object?
.
.
Please note: All this code is for example purposes only. My code is similar, but its handled by a specific controller and specific views to CRUD Categories and CategoryItemProperties and I have no problem with this.
.
.
Sorry for this long text. I've tryed to be as clearest as I can. If you need any more info, drop a comment, please.
Okay rcdmk! first of all my English is terrible and i'm here to just share with you my few experience.
I have build such a complex software in the past with MVVM(WPF) + EF4 + T4 to generate POCO's and i deal with Microsoft Blend to handle with actions, bindings and so on between the client and the viewmodels!
that's work great! i hope i helped you!
ps:
I dont know if Blend supports ASP.Net but creating POCO(Viewmodel) with lazy loading could help u somewhere!
As i understand , Category and corresponding CategoryPropertyItems is describing how Item will be created . Simply Category is drawing an abstract form and item and item properties are concretes (because item property has Value property). So in Item/Create Action (GET) you can build item and it's properties using Category and CategoryPropertyItems.
public Item Build(Category category)
{
Item item = new Item();
item.Category = category;
item.ItemId = ...
foreach(var categoryItemProperty in category.ItemProperties)
{
ItemProperty itemProperty = new ItemProperty();
itemProperty.Item = item;
itemProperty.CategoryItemProperty = categoryItemProperty;
itemProperty.ItemPropertyId = ...
}
return item;
}
In result of Index/Create Action you can use either just this Item object or you can put item into ViewModel .
In View you can bind direcly to model (Item) properties .
This links can help you .
Editing and binding nested lists with ASP.NET MVC 2
Model Binding To A List

Categories