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
Related
I am trying to insert a product into my database with an associated category. One product can belong to several categories and obviously one category can have several products. When I insert, I am sure I am missing something in my controller method but I'm not sure what it is. I have a bridge table called ProductCategory that just has a ProductID and a CategoryID in it. That table is not getting populated when I do the insert.
Here is my controller method that is doing the insert:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult EditProduct([Bind(Include = "ID,itemNumber,product,description,active,PDFName,imageName,SelectedCategories")] ProductModel model)
{
if (ModelState.IsValid)
{
using (var context = new ProductContext())
{
context.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
if (model.ID == 0)
{
// Since it didn't have a ProductID, we assume this
// is a new Product
if (model.description == null || model.description.Trim() == "")
{
model.description = "Our Famous " + model.product;
}
if (model.imageName == null || model.imageName.Trim() == "")
{
model.imageName = model.itemNumber + ".jpg";
}
if (model.PDFName == null || model.PDFName.Trim() == "")
{
model.PDFName = model.itemNumber + ".pdf";
}
Session["dropdownID"] = model.ID;
// I think I probably need some additional code here...
context.Products.Add(model);
}
else
{
// Since EF doesn't know about this product (it was instantiated by
// the ModelBinder and not EF itself, we need to tell EF that the
// object exists and that it is a modified copy of an existing row
context.Entry(model).State = EntityState.Modified;
}
context.SaveChanges();
return RedirectToAction("ControlPanel");
}
}
return View(model);
}
And my Product model:
public class ProductModel
{
public int ID { get; set; }
[Required(ErrorMessage = "Required")]
[Index("ItemNumber", 1, IsUnique = true)]
[Display(Name = "Item #")]
public int itemNumber { get; set; }
[Required(ErrorMessage = "Required")]
[Display(Name = "Product")]
[MaxLength(50)]
public String product { get; set; }
[Display(Name = "Description")]
[MaxLength(500)]
public String description { get; set; }
[DefaultValue(true)]
[Display(Name = "Active?")]
public bool active { get; set; }
[Display(Name = "Image Name")]
public String imageName { get; set; }
[Display(Name = "PDF Name")]
public String PDFName { get; set; }
[Display(Name = "Category(s)")]
public virtual ICollection<CategoryModel> ProductCategories { get; set; }
public int[] SelectedCategories { get; set; }
public IEnumerable<SelectListItem> CategorySelectList { get; set; }
//public ICollection<CategoryModel> CategoryList { get; set; }
public virtual BrochureModel Brochure { get; set; }
public IEnumerable<SelectListItem> BrochureList { get; set; }
[Display(Name = "Category(s)")]
public String CategoryList { get; set; }
public static IEnumerable<SelectListItem> getCategories(int id = 0)
{
using (var db = new ProductContext())
{
List<SelectListItem> list = new List<SelectListItem>();
var categories = db.Categories.ToList();
foreach (var cat in categories)
{
SelectListItem sli = new SelectListItem { Value = cat.ID.ToString(), Text = cat.categoryName };
//if (id > 0 && cat.ID == id)
//{
// sli.Selected = true;
//}
list.Add(sli);
}
return list;
}
}
public ProductModel()
{
active = true;
}
}
And my Category model:
public class CategoryModel
{
public int ID { get; set; }
[Required(ErrorMessage = "Required")]
[Display(Name = "Category Name")]
[MaxLength(50)]
public String categoryName { get; set; }
[MaxLength(50)]
public String categoryDBName { get; set; }
[DefaultValue(true)]
[Display(Name = "Active?")]
public bool isActive { get; set; }
//public virtual ICollection<ProductCategory> ProductCategories { get; set; }
public virtual ICollection<ProductModel> Products { get; set; }
}
Here is my Product context:
public class ProductContext : DbContext
{
public ProductContext()
: base("DefaultConnection")
{
Database.SetInitializer<ProductContext>(new CreateDatabaseIfNotExists<ProductContext>());
}
public DbSet<CategoryModel> Categories { get; set; }
public DbSet<ProductModel> Products { get; set; }
public DbSet<BrochureModel> Brochures { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
//modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
//modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
modelBuilder.Entity<CategoryModel>().ToTable("Categories");
modelBuilder.Entity<ProductModel>().ToTable("Products");
modelBuilder.Entity<BrochureModel>().ToTable("Brochures");
modelBuilder.Entity<ProductModel>()
.HasMany(p => p.ProductCategories)
.WithMany(p => p.Products)
.Map(m =>
{
m.ToTable("ProductCategory");
m.MapLeftKey("ProductID");
m.MapRightKey("CategoryID");
});
//modelBuilder.Entity<CategoryModel>()
//.HasMany(c => c.ProductCategories)
//.WithRequired()
//.HasForeignKey(c => c.CategoryID);
}
public System.Data.Entity.DbSet<newBestPlay.Models.RegisterViewModel> RegisterViewModels { get; set; }
}
Let me know if other code or more info is needed.
You're never doing anything with your SelectedCategories array. You need to use this to pull CategoryModel instance from the database and then associate those with the product.
context.Categories.Where(c => model.SelectedCategories.Contains(c.ID)).ToList()
.ForEach(c => model.ProductCategories.Add(c));
...
context.SaveChanges();
UPDATE
Can I ask how to list out the categories for each product in my view?
That's kind of a loaded question, as it's highly dependent on what type of experience you're trying to achieve. Generally speaking, with any collection, you'll need to iterate over the items in that collection and then render some bit of HTML for each item. You can do this in a number of different ways, which is why there's not really one "right" answer I can give you. However, just to give you an idea and not leave you with no code at all, here's a very basic way to just list out the name of every category:
#string.Join(", ", Model.ProductCategories.Select(c => c.categoryName))
My Model:
public ECmain()
{
this.Notes = new Collection<Notes>();
}
public int ID { get; set; }
public string Auth { get; set; }
public string KeyWords { get; set; }
public string Description { get; set; }
public string URL { get; set; }
public string Category { get; set; }
public string SubCategory { get; set; }
public string Title { get; set; }
public string Live { get; set; }
public virtual ICollection<Notes> Notes { get; set; }
public virtual ICollection<Email> Email { get; set; }
}
public class MyViewModel
{
public IQueryable<Notes> NotesList { get; set; }
public IQueryable<ECmain> ECmainList { get; set; }
public int ECmain.ID { get; set; }
public IQueryable<Email> EmailList { get; set; }
}
My Controller:
// GET: ECmain/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var viewModel = new MyViewModel
{
ECmainList = from m in db.ECmain.Take(10)
where m.ID == id
select m,
NotesList = from n in db.Notes
where n.ECmainID ==id
select n,
EmailList = from e in db.Email
where e.ECmainID ==id
select e
};
// viewModel.NotesList = new
if (viewModel == null)
{
return HttpNotFound();
}
return View(viewModel);
}
My Edit View:
#model EditSuite.Models.MyViewModel
#using (Html.BeginForm())
{
#Html.HiddenFor(model => model.ID )
I want to access the ECmainList.ID The error is
Compiler Error Message: CS1061: 'EditSuite.Models.MyViewModel' does not contain a definition for 'ID'
I tried
#Html.HiddenFor(model => model.ECmainListID.ID )
and
#Html.HiddenFor(Model.model.ECmainListID.ID )
Neither one worked.
It seems pretty obvious from the compiler message:
Compiler Error Message: CS1061: 'EditSuite.Models.MyViewModel' does not contain a definition for 'ID'
So where on the following model is the ID?:
public class MyViewModel
{
public IQueryable<Notes> NotesList { get; set; }
public IQueryable<ECmain> ECmainList { get; set; }
public IQueryable<Email> EmailList { get; set; }
// there is no: public int ID { get; set; } ?
}
I want to access the ECmainList.ID
However, per your model the ECmainList is a IQueryable<ECmain> and the IQueryable<T> also does not have a public property or field called ID. How can you add an editor for a ID of a list of objects? You'd need to loop through the list and have multiple ID fields.
I'm creating new product belonging to the category model.When i execute my Create method, the error invoke that
"Object Reference not set to instance of object" on "model" object
My Controller class:
public ActionResult CreateProduct()
{
SetCateProductViewBag();
return View(new CateProdViewModel());
}
[HttpPost]
public ActionResult CreateProduct(CateProdViewModel model)
{
var ValidImageTypes = new String[]
{
"image/gif",
"image/jpeg",
"image/jpg",
"image/pjpeg",
"image/png"
};
if (model.ImageUpload == null || model.ImageUpload.ContentLength == 0)
{
ModelState.AddModelError("ImageUpload", "This Field is required.");
}
else if (!ValidImageTypes.Contains(model.ImageUpload.ContentType))
{
ModelState.AddModelError("ImageUload", "Please choose either a GIF,jpg or png type of file.");
}
if (ModelState.IsValid)
{
var prod = new Product
{
// CategoryName =model.category.CategoryName,
//CategoryDescription=model.category.CategoryDescription,
//CategoryId=model.CategoryId,
ProductName = model.ProductName,
ProductDescription = model.ProductDescription,
Model = model.Model,
ProductPrice = model.ProductPrice,
AvailableForSale = model.AvailableForSale,
Shippable = model.Shippable,
AvailableStock = model.AvailableStock,
ProductPicture = model.ProductPicture
};
SetCateProductViewBag(prod.CategoryId);
if (model.ImageUpload != null && model.ImageUpload.ContentLength > 0)
{
var UploadDir = "~/Uploads";
var ImagePath = Path.Combine(Server.MapPath(UploadDir), model.ImageUpload.FileName);
var ImageUrl = Path.Combine(UploadDir, model.ImageUpload.FileName);
model.ImageUpload.SaveAs(ImagePath);
prod.ProductPicture = ImageUrl;
}
productContext.product.Add(prod);
productContext.SaveChanges();
return RedirectToAction("CategoryIndex");
}
return View(model);
}
CateProdViewModel class:
public class CateProdViewModel
{
public int CategoryId { get; set; }
public string CategoryName { get; set; }
public string CategoryDescription { get; set; }
public string ProductName { get; set; }
public string ProductDescription { get; set; }
public int AvailableStock { get; set; }
public decimal ProductPrice { get; set; }
public string Model { get; set; }
public bool AvailableForSale { get; set; }
public bool Shippable { get; set; }
[DataType(DataType.ImageUrl)]
public string ProductPicture { get; set; }
public int SelectedValue { get; set; }
[DataType(DataType.Upload)]
public HttpPostedFileBase ImageUpload { get; set; }
public virtual Category category { get; set; }
[Display(Name="Product Categories")]
public virtual ICollection<Category> categories { get; set; }
}
Entity classes for Category and Product:
public class Product
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public string ProductDescription { get; set; }
public int AvailableStock { get; set; }
public decimal ProductPrice { get; set; }
public string Model { get; set; }
public bool AvailableForSale { get; set; }
public bool Shippable { get; set; }
public string ProductPicture { get; set; }
public int CustomerId { get; set; }
public int CategoryId { get; set; }
public virtual Category category { get; set; }
}
public class Category
{
public int CategoryId { get; set; }
public string CategoryName { get; set; }
public string CategoryDescription { get; set; }
public virtual ICollection<Product> product { get; set; }
}
My View:
#model SmartShoppingCart.Models.CateProdViewModel
#{
ViewBag.Title = "CreateProduct";
}
<h2>Create Product</h2>
<form action="" method="post" enctype="multipart/form-data">
<div>
#Html.LabelFor(m=>m.CategoryId,"Category")
#Html.DropDownList("CategoryId",ViewBag.categories as SelectList, string.Empty)
#Html.ValidationMessageFor(m=>m.CategoryId)
</div>
<div>
#Html.HiddenFor(m=>m.CategoryId)
</div>
<div>
#Html.LabelFor(m=>m.ProductName)
#Html.TextBoxFor(m=>m.ProductName)
#Html.ValidationMessageFor(m=>m.ProductName)
</div>
<div>
#Html.LabelFor(m=>m.ProductDescription)
#Html.TextBoxFor(m=>m.ProductDescription)
#Html.ValidationMessageFor(m=>m.ProductDescription)
</div>
<div>
#Html.LabelFor(m=>m.Model)
#Html.TextBoxFor(m=>m.Model)
#Html.ValidationMessageFor(m=>m.Model)
</div>
<div>
#Html.LabelFor(m=>m.ProductPrice)
#Html.TextBoxFor(m=>m.ProductPrice)
#Html.ValidationMessageFor(m=>m.ProductPrice)
</div>
<div>
#Html.LabelFor(m=>m.AvailableForSale)
#Html.TextBoxFor(m=>m.AvailableForSale)
#Html.ValidationMessageFor(m=>m.AvailableForSale)
</div>
<div>
#Html.LabelFor(m=>m.Shippable)
#Html.TextBoxFor(m=>m.Shippable)
#Html.ValidationMessageFor(m=>m.Shippable)
</div>
<div>
#Html.LabelFor(m=>m.AvailableStock)
#Html.TextBoxFor(m=>m.AvailableStock)
#Html.ValidationMessageFor(m=>m.AvailableStock)
</div>
<div>
#Html.LabelFor(m=>m.ImageUpload)
#Html.TextBoxFor(m => m.ImageUpload, new {type="file" })
</div>
<div>
<button type="submit" value="Add" ></button>
</div>
</form>
private void SetCateProductViewBag(int? CateId = null)
{
if (CateId == null)
{
ViewBag.Categories = new SelectList(productContext.category, "CategoryId", "CategoryName");
}
else
{
ViewBag.Categories = new SelectList(productContext.category.ToArray(),"CategoryId","CategoryName",CateId);
}
}
Your error occurs because your model is null on post back. The model is null because it contains a property named Model and the parameter name of the action method is also named model which is confusing the poor DefaultModelBinder. Either change the name of the property to something else or change the parameter name in your post action method to something else.
Why don't you change the first action to:
public ActionResult CreateProduct(CateProdViewModel model)
{
SetCateProductViewBag();
return View(model);
}
And your DefaultModelBinder will instantiate the model so it's never null.
Other than that, I need to know what you mean by saying When i execute my Create method. Since you don't have a Create method there. You have CreateProduct and you got 2 of them. Is this a POST or GET request?
Im having trouble linking my loaned items to my Library for each customer. It does it fine when it goes through the "AddToLibrary" method but when it comes to retreiving it, the medialibrary is empty and the query in the IEnumerable<Item> ItemsOnLoan method is returning null. This is a very basic ASP.NET MVC 4 application and im very new to this so its probably something silly ive missed out.
I just want to be able to add an item to the loaned items table, have the list of loaned items for each customer appear in their personal Library (defined in model) and then retreive the list of their items. Below is all the code and I am using a code first approach. Thank you :)
Model
public class Customer
{
public int Id { get; set; }
public string ForeName { get; set; }
public string SurName { get; set; }
public Address address { get; set; }
public string Email { get; set; }
public string Telephone { get; set; }
public string Mobile { get; set; }
public List<LoanedItem> Library { get; set; }
public Customer()
{
if (Library == null || Library.Count == 0)
{
Library = new List<LoanedItem>();
}
}
public IEnumerable<Item> ItemsOnLoan
{
get
{
var items = (from i in Library
where i.Customer.Id == this.Id
select i).OfType<item>();
return items;
}
}
}
Loaned Item model
public class LoanedItem
{
public int? Id { get; set; }
public Customer Customer { get; set; }
public MediaItem Item { get; set; }
}
ItemController --> adding to library method
public ActionResult AddToLibrary(int id)
{
Item libraryItem = db.Items.Find(id);
Customer c = db.Customers.Find(1);
LoanedItem newLoanGame = new LoanedItem()
{
Customer = c,
Item = libraryItem
};
db.LoanedItems.Add(newLoanGame);
db.SaveChanges();
return RedirectToAction("Index");
}
Customer Controller
public ActionResult ViewProfile(int id = 1)
{
Customer c = db.Customers.Find(id);
if (c == null)
{
return HttpNotFound();
}
return View(c);
}
public ActionResult GetLibraryItems(int id = 1)
{
var items = db.Customers.Find(id).ItemsOnLoan;
return View(items);
}
Context
public class LibraryContext : DbContext
{
public DbSet<Address> Addresses { get; set; }
public DbSet<Customer> Customers { get; set; }
public DbSet<LoanedItem> LoanedItems { get; set; }
public DbSet<Item> Items { get; set; }
public LibraryContext()
: base("LbContext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new CustomerConfiguration());
modelBuilder.Configurations.Add(new LoanedItemConfiguration());
modelBuilder.Entity<Item>();
modelBuilder.Entity<Address>();
base.OnModelCreating(modelBuilder);
}
}
Assuming that Proxy generation is enabled try this:
public class Customer
{
public int Id { get; set; }
public string ForeName { get; set; }
public string SurName { get; set; }
public Address address { get; set; }
public string Email { get; set; }
public string Telephone { get; set; }
public string Mobile { get; set; }
public virtual ICollection<LoanedItem> ItemsOnLoan { get; set; }
public Customer()
{
}
}
using this to acccess:
public ActionResult GetLibraryItems(int id = 1)
{
var customer = db.Customers.Find(id);
if (customer != null)
{
var items = customer.ItemsOnLoan;
return View(items);
}
//handle not found or throw an exception
throw new Exception();
}
follow this link for more information on Proxies and Lazy Loading.
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!