I have the following models:
public class Page
{
public int PageID { get; set; }
public string Name { get; set; }
public string Content { get; set; }
public DateTime? DateCreated { get; set; }
public bool IsPublished { get; set; }
public string ModifiedBy { get; set; }
public DateTime? DateModified { get; set; }
public int UserID { get; set; }
public int CategoryID { get; set; }
public virtual User User { get; set; }
public virtual Category Category { get; set; }
}
public class Category
{
public int CategoryID { get; set; }
[Required(ErrorMessage = "Category name is required.")]
[Display(Name = "Category Name")]
public string Name { get; set; }
public virtual ICollection<Page> Pages { get; set; }
}
and I want to populate this navigation list:
<div id="centeredmenu" class="nav-border nav-color">
<ul>
#foreach (var pages in Model)
{
<li>CATEGORY NAME GOES HERE
<ul>
#foreach (var pages in Model)
{
<li>PAGE NAMES GO HERE</li>
}
</ul>
</li>
}
</ul>
</div>
but I'm having problems implementing the controller. I tried this ViewModel:
public class MainPageModels
{
public Category Categories { get; set; }
public Page Pages { get; set; }
}
but it just confused me even more with this error message:
System.Data.Edm.EdmEntityType: : EntityType 'MainPageModels' has no key defined. Define the key for this EntityType.
System.Data.Edm.EdmEntitySet: EntityType: EntitySet �MainModels� is based on type �MainPageModels� that has no keys defined.
This is my controller:
public ActionResult Index()
{
var pages = db.MainModels.Select(p => p.Pages).Select(c => c.Category);
return View(pages);
}
I may be missing something simple here.
Posting this here for the code/syntax
public class Person
{
[Key]
public int PersonID { get; set; }
public string Name { get; set; }
public string LastName { get; set; }
}
public class DataContext : DbContext
{
EntitySet<Person> Persons { get; set; }
}
Your View Model can then do the following
public class PersonAddViewModel
{
public string Name { get; set; }
public string LastName { get; set; }
public void CreateViewModelFromDataModel(Person person)
{
this.Name = person.Name;
this.LastName = person.LastName ;
}
}
This is just an example, just to show the difference between a Data Model and a View Model
Your View would then be a strongly typed view of PersonAddViewModel
Here my solution to my parent-child list problem:
I created a ViewModel to house both my categories and pages:
public class HomeViewModels
{
[Key]
public int HomeViewKey { get; set; } //This is a MUST!
public IEnumerable<Category> ViewCategories { get; set; }
public IEnumerable<Page> ViewPages { get; set; }
public void CreateHomeViewModel(IEnumerable<Category> categories,
IEnumerable<Page> pages)
{
this.ViewCategories = categories;
this.ViewPages = pages;
}
}
Then edited my controller to populate the viewmodel:
public ActionResult Index()
{
HomeViewModels homePages = new HomeViewModels();
homePages.CreateHomeViewModel(db.Categories.ToList(),
db.Pages.ToList());
return View(homePages);
}
and finally creating the ul-li lists with the following:
#{var hvCategories = Model.ViewCategories;}
#foreach (var categories in hvCategories)
{
<li>#Html.ActionLink(categories.Name, "Index", "Home")
<ul>
#{var hvPages = Model.ViewPages
.Where(p => p.CategoryID == categories.CategoryID);}
#foreach (var pages in hvPages)
{
<li>#Html.ActionLink(pages.Name, "Index", "Home")</li>
}
</ul>
</li>
I hope this helps anyone who plans to build a nested list using a parent-child model. This took me two days to figure out. Cheers!
Related
I've problem with creating new record to empty tables on SQLServer
When trying to pass new record I get 'Object reference not set to an instance of an object.' error
When I try edit existing record, tables display content properly, but save changes won't work. With Guests table it will only reload old entry without changes, and for Contacts it's returning same error as on creating new record.
App should let create and edit record by displaying as partial view editor forms for each table.
I'm beginner in MVC.
Below is my code.
Tables models:
[Table("GuestsTest")]
public class Guest
{
[Key]
[HiddenInput(DisplayValue = false)]
public int GuestID { get; set; }
public string GuestLastName { get; set; }
public string GuestFirstName { get; set; }
public string GuestMiddleName { get; set; }
public string GuestEmail { get; set; }
public string GuestSex { get; set; }
}
[Table("ContactsTest")]
public class Contact
{
[Key]
[HiddenInput(DisplayValue = false)]
public int ContactID { get; set; }
[HiddenInput(DisplayValue = false)]
public int GuestID { get; set; }
public int PostalCode { get; set; }
public string City { get; set; }
public string Street { get; set; }
public string HouseNumber { get; set; }
public string PhoneNumber { get; set; }
My view model
public class TableViewModel
{
public Guest GetGuest { get; set; }
public Contact GetContact { get; set; }
}
My controllers
public class AdminController : Controller
{
private IGuestRepository guestRepository;
private IContactRepository contactRepository;
private IQRCodeRepository qrcodeRepository;
public AdminController(IGuestRepository repoG, IContactRepository repoC, IQRCodeRepository repoQ)
{
guestRepository = repoG;
contactRepository = repoC;
qrcodeRepository = repoQ;
}
public ActionResult Index()
{
return View(guestRepository.Guests);
}
public ActionResult EditGuest(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
TableViewModel viewModel = new TableViewModel();
viewModel.GetGuest = guestRepository.Guests.FirstOrDefault(g => g.GuestID == id);
viewModel.GetContact = contactRepository.Contacts.FirstOrDefault(c => c.ContactID == id);
if (viewModel.GetGuest == null)
{
return HttpNotFound();
}
return View(viewModel);
}
public ActionResult GuestForm(int? id)
{
var viewModel = new TableViewModel();
viewModel.GetGuest = guestRepository.Guests.FirstOrDefault(g => g.GuestID == id);
return PartialView("_GuestForm", viewModel.GetGuest);
}
[HttpPost]
public ActionResult GuestForm(TableViewModel getGuest)
{
if (ModelState.IsValid)
{
guestRepository.SaveGuest(getGuest.GetGuest);
qrcodeRepository.CreateQRCode(getGuest.GetGuest);
TempData["message"] = string.Format("Zapisano {0} {1}", getGuest.GetGuest.GuestFirstName, getGuest.GetGuest.GuestLastName);
return RedirectToAction("EditGuest/" + getGuest.GetGuest.GuestID);
}
else
{
return PartialView(getGuest.GetGuest);
}
}
public ActionResult ContactForm(int? id)
{
var viewModel = new TableViewModel();
viewModel.GetContact = contactRepository.Contacts.FirstOrDefault(c => c.ContactID == id);
return PartialView("_ContactForm", viewModel.GetContact);
}
[HttpPost]
public ActionResult ContactForm(TableViewModel getGuest)
{
if (ModelState.IsValid)
{
contactRepository.SaveContact(getGuest.GetContact);
TempData["message"] = string.Format("Zapisano {0} {1}", getGuest.GetGuest.GuestFirstName, getGuest.GetGuest.GuestLastName);
return RedirectToAction("EditGuest/" + getGuest.GetGuest.GuestID);
}
else
{
return PartialView(getGuest.GetContact);
}
}
public ActionResult Create()
{
return View("EditGuest", new TableViewModel());
}
My view
#model MSConference.WebUI.Models.TableViewModel
#{
if (Model.GetGuest.GuestEmail == null)
{
ViewBag.Title = "Tworzenie nowego użytkownika";
}
else
{
ViewBag.Title = "Edycja";
}
Layout = "~/Views/Shared/_AdminLayout.cshtml";
}
#if (Model.GetGuest.GuestEmail == null)
{
<h2>Tworzenie nowego użytkownika</h2>
}
else
{
<h2>Edycja - #Model.GetGuest.GuestFirstName #Model.GetGuest.GuestLastName</h2>
}
#using (Html.BeginForm("EditGuest", "Admin"))
{
#Html.AntiForgeryToken()
<div class="container">
<ul class="nav nav-pills">
<li class="active"><a data-toggle="pill" href="#EditGuest">Edycja - Gość</a></li>
<li><a data-toggle="pill" href="#EditContact">Edycja - Kontakt</a></li>
<li><a data-toggle="pill" href="#EditBill">Edycja - Rezerwacja</a></li>
<li><a data-toggle="pill" href="#EditPlan">Edycja - Konferencja</a></li>
</ul>
<div class="tab-content">
<div id="EditGuest" class="tab-pane fade in active">#Html.Partial("_GuestForm", new MSConference.WebUI.Models.TableViewModel())</div>
<div id="EditContact" class="tab-pane fade">#Html.Partial("_ContactForm", new MSConference.WebUI.Models.TableViewModel())</div>
<div id="EditBill" class="tab-pane fade">sgdg</div>
<div id="EditPlan" class="tab-pane fade">gsdgsgsgsg</div>
</div>
</div>
}
<div>
#Html.ActionLink("Powrót do Listy", "Index", null, new { #class = "btn btn-success" })
</div>
I tried every method of passing model I could find and understand
EDIT
Here are my Repositories. Create error comes from if (contact.ContactID == 0)
public class EFGuestRepository : IGuestRepository
{
private EfDbContext context = new EfDbContext();
public IEnumerable<Guest> Guests
{
get { return context.Guests; }
}
public void SaveGuest(Guest guest)
{
if (guest.GuestID == 0)
{
context.Guests.Add(guest);
}
else
{
Guest dbEntry = context.Guests.Find(guest.GuestID);
if (dbEntry != null)
{
dbEntry.GuestLastName = guest.GuestLastName;
dbEntry.GuestFirstName = guest.GuestFirstName;
dbEntry.GuestMiddleName = guest.GuestMiddleName;
dbEntry.GuestEmail = guest.GuestEmail;
dbEntry.GuestSex = guest.GuestSex;
}
}
context.SaveChanges();
}
public Guest DeleteGuest(int guestId)
{
Guest dbEntry = context.Guests.Find(guestId);
if (dbEntry != null)
{
context.Guests.Remove(dbEntry);
context.SaveChanges();
}
return dbEntry;
}
}
public class EFContactRepository : IContactRepository
{
private EfDbContext context = new EfDbContext();
public IEnumerable<Contact> Contacts
{
get { return context.Contacts; }
}
public void SaveContact(Contact contact)
{
if (contact.ContactID == 0)
{
contact.GuestID = contact.ContactID;
context.Contacts.Add(contact);
}
else
{
Contact dbEntry = context.Contacts.Find(contact.ContactID);
if (dbEntry != null)
{
contact.GuestID = contact.ContactID;
dbEntry.PostalCode = contact.PostalCode;
dbEntry.City = contact.City;
dbEntry.Street = contact.Street;
dbEntry.HouseNumber = contact.HouseNumber;
dbEntry.PhoneNumber = contact.PhoneNumber;
}
}
context.SaveChanges();
}
public Contact DeleteContact(int guestId)
{
Contact dbEntry = context.Contacts.Find(guestId);
if (dbEntry != null)
{
context.Contacts.Remove(dbEntry);
context.SaveChanges();
}
return dbEntry;
}
public interface IGuestRepository
{
IEnumerable<Guest> Guests { get; }
void SaveGuest(Guest guest);
Guest DeleteGuest(int guestId);
}
public interface IContactRepository
{
IEnumerable<Contact> Contacts { get; }
void SaveContact(Contact guest);
Contact DeleteContact(int guestId);
}
I've built whole project working with Adam Freeman pro asp.net mvc 5 book (SportsStore project).
Passing entities to the view isn't a good practice, and depending on what you do with them when they are returned from the view, this can expose you to data tampering. Your "TableViewModel" should just consist of the flattened fields from the guest and contact, or a GuestViewModel and ContactViewModel revealing only the keys and details you need to display/edit. Entities are designed to be associated to a DbContext. Putting a reference to them in a view model is orphaning them. When you pass them back to the controller, they become just POCO instances that are deserialized from the JSON data coming from the view. They have no change tracking etc. that you might expect from using entities while they're freshly loaded from a DbContext. You can attach them to a DbContext, but you would have to manually set the entity State to "Modified" otherwise the context does not know the entity has been changed.
Your issue as it stands right now will probably be in what your SaveGuest method is doing.
The typical MVC lifecycle for the data would be roughly:
View:
Load entity(ies) from context
Populate view models
Pass to view.
Update:
Validate view model against current session
Load entity(ies) from context based on keys
Check that view model isn't stale (last mod date / timestamp / row version matches)
Validate and copy across only details that can be updated from view model into entity
SaveChanges.
Chances are if you're not seeing changes, you're probably attaching the entity to the new context without setting the entity State to "Modified". Note that this is not recommended as you are unconditionally trusting the data coming from the client. For instance you may intend to only see that a user has modified data that you created controls for, but by attaching the entity, you leave the door open for the POST call to be intercepted or played back with any/all data on the entity being altered. You would need to load the existing entity anyways to validate that nothing that shouldn't have been changed had been changed. Another possibility is you could be reloading the entity without realizing, not copying the values across from your view model's entity before calling SaveChanges, or adding the entity to the context thinking it would update the existing row, but it is saving a completely new row with new PK.
I solved my problem by replacing
#Html.Partial("PartialView", Model)
With
#{ Html.RenderPartial("PartialView", Model);}
I also rebuilded my models
Now my entity models looks like this:
[Table("GuestsTest")]
public class Guest
{
[Key]
public int GuestID { get; set; }
public string GuestLastName { get; set; }
public string GuestFirstName { get; set; }
public string GuestMiddleName { get; set; }
public string GuestEmail { get; set; }
public string GuestSex { get; set; }
[Required]
public virtual Contact Address { get; set; }
}
[Table("ContactsTest")]
public class Contact
{
public int ContactID { get; set; }
[Key, ForeignKey("Guest")]
public int GuestID { get; set; }
public int PostalCode { get; set; }
public string City { get; set; }
public string Street { get; set; }
public string HouseNumber { get; set; }
public string PhoneNumber { get; set; }
public virtual Guest Guest { get; set; }
}
And my view models got full rebuild to this:
public class TableViewModel
{
public GuestViewModel GetGuest { get; set; }
public ContactViewModel GetContact { get; set; }
}
public class GuestViewModel
{
[Key]
[HiddenInput(DisplayValue = false)]
public int? GuestID { get; set; }
[MaxLength(50)]
[Required(ErrorMessage = "Proszę podać nazwisko.")]
[Display(Name = "Nazwisko")]
public string GuestLastName { get; set; }
[MaxLength(50)]
[Required(ErrorMessage = "Proszę podać imię.")]
[Display(Name = "Imię")]
public string GuestFirstName { get; set; }
[MaxLength(50)]
[Display(Name = "Drugie imię")]
public string GuestMiddleName { get; set; }
[MaxLength(50)]
[Required(ErrorMessage = "Proszę podać adres email.")]
[RegularExpression(".+\\#.+\\..+", ErrorMessage = "Proszę podać prawidłowy adres e-mail.")]
[Display(Name = "Email")]
public string GuestEmail { get; set; }
[MaxLength(1)]
[Required(ErrorMessage = "Proszę podać płeć.")]
public string GuestSex { get; set; }
}
public class ContactViewModel
{
[HiddenInput(DisplayValue = false)]
public int ContactID { get; set; }
[Key, ForeignKey("Guest")]
[HiddenInput(DisplayValue = false)]
public int GuestID { get; set; }
[Required(ErrorMessage = "Proszę podać kod pocztowy.")]
[Display(Name = "Kod pocztowy")]
public int PostalCode { get; set; }
[Required(ErrorMessage = "Proszę podać Miejscowość.")]
[Display(Name = "Miejscowość")]
public string City { get; set; }
[Required(ErrorMessage = "Proszę podać ulicę.")]
[Display(Name = "Ulica")]
public string Street { get; set; }
[Required(ErrorMessage = "Proszę podać numer domu/mieszkania.")]
[Display(Name = "Numer domu/mieszkania")]
public string HouseNumber { get; set; }
[Required(ErrorMessage = "Proszę podać numer telefonu.")]
[Display(Name = "Numer telefonu")]
public string PhoneNumber { get; set; }
}
Last, I overloaded my save function to work with new models
I've done this EF MVC Application (Code First) with listing/editing/deleting functions. Everything works fine, but now I need to add two dropdown fields. Product has a category and a subcategory which needed to be edited. This is what I have so far:
Main class where ProductSubcategoryID is a foreign key
public class Product
{
public int ProductID { get; set; }
public string Name { get; set; }
public string ProductNumber { get; set; }
public int? ProductSubcategoryID { get; set; }
public IEnumerable<SelectList> SelectedCat = new List<SelectList> {};
public IEnumerable<SelectList> SelectedSubCat = new List<SelectList> {};
}
public class ProductCategory
{
public int ProductCategoryID { get; set; }
public string Name { get; set; }
}
public class ProductSubcategory
{
public int ProductSubcategoryID { get; set; }
public int ProductCategoryID { get; set; }
public string Name { get; set; }
}
On the Product controller class I have:
public ActionResult Create()
{
ViewBag.SubcatSelection = new SelectList(dbSubcat.ProductSubcategories, "ProductSubcategoryID", "Name"); ;
return View();
}
and on Edit:
#Html.LabelFor(model => model.ProductSubcategoryID)
#Html.DropDownListFor(model => model.SelectedSubCat, ViewBag.SubcatSelection as SelectList, "ProductSubcategoryID", "Name");
The result:
There is no ViewData item of type 'IEnumerable<SelectListItem>' that has the key 'SelectedSubCat'.
I have customer model
public class Customer
{
public Customer()
{
this.SystemValues = new HashSet<SystemValue>();
}
public string Name { get; set; }
public Nullable<System.Guid> GUID { get; set; }
public int Id { get; set; }
public virtual ICollection<SystemValue> SystemValues { get; set; }
}
and systemValue model
public class SystemValue
{
public int CustomerId { get; set; }
public int SystemValueId { get; set; }
public Nullable<int> SystemValueCategoryId { get; set; }
public string SystemValueType { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string TextValue { get; set; }
public Nullable<int> IntValue { get; set; }
public Nullable<double> FloatValue { get; set; }
public byte[] BlobValue { get; set; }
public Nullable<System.DateTime> DateTimeValue { get; set; }
public Nullable<bool> BooleanValue { get; set; }
public Nullable<int> LookupValueId { get; set; }
public Nullable<int> LookupValueGroupId { get; set; }
public Nullable<bool> IsReadonly { get; set; }
public bool IsHidden { get; set; }
public int Id { get; set; }
public virtual Customer Customers { get; set; }
}
in which way I could show a link in CustomerView(CustomersController) foreach customer that redirect to the SystemValuesView(SystemValuesController) with related to this customer SystemValues?
I found out one way - redirect to this controller's action with parameter.
public ActionResult ViewSystemValues(int? id)
{
return RedirectToAction("Index", "SystemValues", new {id});
}
But I'm sure there must be smarter way.
#for(int i = 0; i < Model.yourcustomerlist.Count(); i++)
{
<tr>
<td>
<a class="btn btn-primary" href="#Url.Action("Index", "SystemValues", new { id = Model.yourcustomerlist[i].CustomerId })">
<b>Go to system values</b>
</a>
</td>
</tr>
}
I hope I understood you correctly.
This code should go in the view. The view should be strongly typed to a model.
Code is if you want a button that redirects to the index view of the SystemValues controller, with the CustomerId as input. You should change "yourcustomerlist" to the list containing the customer information. If it's not part of a table, remove the table-related tags (<td> and <tr>).
I am trying the get the products displayed for a category. I got this error message: Sequence contains no elements. I got a table product and a table category. Each product is identified to a category.
HomeController.cs
public ActionResult Browse(string nomCategorie)
{
var categorieModel = db.Categories.Include("Produits") <== the error happened here
.Single(g => g.NomCategorie == nomCategorie);
return View(categorieModel);
}
Browse.cshtml
#model MVCWebStore.Models.Categorie
#{
ViewBag.Title = "Browse";
}
<h2>Browse Categorie: #Model.NomCategorie</h2>
<ul>
#foreach (var produit in Model.Produits)
{
<li>
#produit.Description
</li>
}
</ul>
Produit.cs
namespace MVCWebStore.Models
{
using System;
using System.Collections.Generic;
public partial class Produit
{
public Produit()
{
this.ItemPaniers = new HashSet<ItemPanier>();
}
public int IdProduit { get; set; }
public int IdCategorie { get; set; }
public string NomProduit { get; set; }
public string Description { get; set; }
public double Prix { get; set; }
public int Quantite { get; set; }
public virtual Categorie Categorie { get; set; }
public virtual ICollection<ItemPanier> ItemPaniers { get; set; }
}
}
categorie.cs
namespace MVCWebStore.Models
{
using System;
using System.Collections.Generic;
public partial class Categorie
{
public Categorie()
{
this.Produits = new HashSet<Produit>();
}
public int IdCategorie { get; set; }
public string NomCategorie { get; set; }
public virtual ICollection<Produit> Produits { get; set; }
}
}
There is a good chance your datacontext is not getting loaded correctly.
Might be something wrong with connectionsting. make sure the datacontext is not null.
If the problem is not with that, chances are tables are not created/linked correctly.
Also EF is weird around the children data a bit. definitely not the greatest framework.
i want to get the name of model genre and title of model list in the partial view but #genre.Lists.Title doesn't work
this is my genre model
public class Genre
{
public int GenreId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public List<List> Lists { get; set; }
}
and this is my List model
[Bind(Exclude = "ListId")]
public class List
{
[ScaffoldColumn(false)]
public int ListId { get; set; }
[DisplayName("Genre")]
public int GenreId { get; set; }
[DisplayName("Maker")]
public int MakerId { get; set; }
[Required(ErrorMessage = "An List Title is required")]
[StringLength(160)]
public string Title { get; set; }
[Required(ErrorMessage = "Price is required")]
[Range(0.01, 100.00,ErrorMessage = "Price must be between 0.01 and 100.00")]
public decimal Price { get; set; }
[DisplayName("List URL")]
[StringLength(1024)]
public string ListUrl { get; set; }
public Genre Genre { get; set; }
public Maker Maker { get; set; }
public virtual List<OrderDetail> OrderDetails { get; set; }
}
and this my actionResult
public ActionResult Navbar()
{
var genres = storeDB.Genres.Include("Lists").ToList();
return PartialView("Navbar",genres);
}
and this is my PartialView
#model IEnumerable<Store.Models.Genre>
#foreach (var genre in Model)
{
#genre.Name
#genre.Lists.Title
}
#genre.Lists is of type List<List>, not List (by the way, I would rename your class somehow, it's easy to confuse with the standard library class of this name).
So you either need another foreach loop to iterate over #genre.Lists or you can get the first element with #genre.Lists[0].Title. It's up to you what you actually want to achieve. For example, you could use string.Join:
#model IEnumerable<Store.Models.Genre>
#foreach (var genre in Model)
{
<text>
#genre.Name
#string.Join(", ", genre.Lists.Select(x => x.Title))
</text>
}
Or write some real HTML. Again, depends what you want your output to be.