I get an exception
Object reference not set to an instance of an object
in ASP.NET MVC when I post form-data with more than one models.
This is my "ViewModel" class:
public class CustomerViewModel
{
public Customer Customer { get; set; }
public IEnumerable<tblGender> Genders { get; set; }
public IEnumerable<tblCountry> Countries { get; set; }
}
This is the Edit action method:
[HttpGet]
public ActionResult Edit(int id)
{
var customer = _context.Customers
.FirstOrDefault(c => c.Id == id);
var viewModel = new CustomerViewModel()
{
Customer = customer,
Genders = _context.tblGenders.ToList(),
Countries = _context.tblCountries.ToList()
};
return View("Edit",viewModel);
}
[HttpPost]
public ActionResult Edit(Customer customer)
{
var cust = _context.Customers.Single(c => c.Id == customer.Id);
TryUpdateModel(cust);
_context.SaveChanges();
return RedirectToAction("Index", "Customer");
}
Create.cshtml:
The error occurs in this section
<div class="form-group">
#Html.LabelFor(m => m.Customer.Gender)
#Html.DropDownListFor(m => m.Customer.Gender, new SelectList(Model.Genders, "Id", "GenderName"), "Select Gender", new { #class = "form-control" })
</div>
In this piece of code, your variable customer can be null:
var customer = _context.Customers.FirstOrDefault(c => c.Id == id);
In this piece of code, you are assigning that very variable to your model.Customer:
var viewModel = new CustomerViewModel()
{
Customer = customer,
In this piece of code, you are using model.Customer as if you are sure it is not null:
#Html.LabelFor(m => m.Customer.Gender)
Out of many other possibilities, this is the most obvious null-ref I can find.
To fix it you can do something like this:
var viewModel = new CustomerViewModel()
{
Customer = customer ?? new Customer(),
or this:
var customer = _context.Customers.FirstOrDefault(c => c.Id == id);
if (customer == null) {
return view("CustomerNotFound"); //or some other way to display your error
}
Regarding the Object reference exception. Have you created instances of the objects that you're using?
e.g:
private readonly CustomerViewModel _customerViewModel;
_customerViewModel = new CustomerViewModel();
Related
I am trying to populate a drop-down list. Initially, everything runs as I expect it to. But, when I navigate away from the original view and come back, I get this error:
System.NullReferenceException: 'Object reference not set to an
instance of an object.' System.Web.Mvc.WebViewPage.Model.get
returned null.
I tried adding an if statement to account for a null model but that did not work. (I took it out for now)
Thank you in advance.
Model:
public class IndexDates
{
public List<PeriodEndDates> ListForDropDown { get; set; }
}
Controller:
public ActionResult Index(IndexDates datesParam)
{
using (PayrollEntities2 db = new PayrollEntities2())
{
DateTime todaysDate = DateTime.Now;
var model = new IndexDates()
{
ListForDropDown = db.PeriodEndDates2
.Where(x => x.periodEndDate <= todaysDate)
.OrderByDescending(x => x.periodEndDate )
.ToList()
};
return View(model);
}
}
View:
#using (Html.BeginForm("Index", "Home", FormMethod.Post))
{
<div class="schedule-bottom">
<p>Period End Date: #Html.DropDownListFor(model => model.ListForDropDown, new SelectList(Model.ListForDropDown, "periodEndDate", "periodEndDate"), "Select...", new { onChange = "selectedtext(this)" })</p>
</div>
}
PayrollEntities2:
public partial class PayrollEntities2 : DbContext
{
public PayrollEntities2()
: base("name=PayrollEntities2")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<PeriodEndDates2> PeriodEndDates2 { get; set; }
}
Home Controller Redirect:
[HttpPost]
public ActionResult Authorize(UsersAndDates userModel)
{
using (MasterTable db = new MasterTable())
{
var userDetails = db.PaybillUsers.Where(x => x.userName == userModel.userName && x.password == userModel.password).FirstOrDefault();
var datesToDisplay = db.PeriodEndDates2
.Where(x => x.ffsPayDate > DateTime.Now)
.Select(x => new PayDates
{
PayDate = x.ffsPayDate,
PeriodEndDate = x.periodEndDate
})
.FirstOrDefault();
if (userDetails == null)
{
return View("Login", userModel);
}
else
{
Session["userID"] = userDetails.userName.ToString();
Session["payDate"] = datesToDisplay.PayDate.ToString();
Session["endDate"] = datesToDisplay.PeriodEndDate.ToString();
return RedirectToAction("Index", "Dashboard");
}
}
}
I think it's because you use using (PayrollEntities2 db = new PayrollEntities2())
instead of just declaring it.
Need to create the edit View with selected Checkboxes from createView
public class CustomerTypeViewModel
{
[Required]
public int? Id { get; set; }
public string Description { get; set; }
[Display(Name = "Please select the Type")]
public bool Selected { get; set; }
}
CustomerView when i should load a list of CustomerTypeViewModel
public List<CustomerTypeViewModel> SelectedCustomerTypes { get; set; }
private List<CustomerTypeViewModel> selectionList = new List<CustomerTypeViewModel>();
public CustomerViewModel()
{
SelectedCustomerTypes = new List<CustomerTypeViewModel>();
}
public void SetCustomerTypeViewModel(IEnumerable<CustomerTypeViewModel> selected, IEnumerable<CustomerTypeViewModel> all)
{
SelectedCustomerTypes.Clear();
foreach (var item in all)
{
SelectedCustomerTypes.Add(item);
}
foreach (var item in selected)
{
SelectedCustomerTypes.FirstOrDefault(x => x.Description == item.Description).Selected = true;
}
}
public List<CustomerTypeViewModel> GetTipi()
{
return selectionList;
}
In controller i should call a method that get customertypes from manager
public CustomerTypeViewModel GetCustomerType(int? customerId)
{
var query = "SELECT * FROM CustomerType where Id = #Id";
return context.GetObject<CustomerTypeViewModel>(query, new { Id = customerId });
}
Now in the Edit at the Controller
[AuthorizeRoles(RoleNames.CanEditCustomer)]
public ActionResult Edit(int? id, int? customerId)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var customer = customerManager.Get(customerId);
var vm = new CustomerViewModel();
vm.SetCustomerTypeViewModel(new List<CustomerTypeViewModel>(), customerTypeManager.GetAll());
if (customer == null)
{
return HttpNotFound();
}
return View(customer);
}
I was told to create a method in manager GetCustomerType(customerId)
<-- this is the id from db table where checkboxes are saved and this method load a list of CustomerTypeViewModel
Now Edit View
<div class="form-group">
#{
for (int i = 0; i < Model.SelectedCustomerTypes.Count(); i++)
{
<div class="col-md-10">
#Html.Label(Model.SelectedCustomerTypes[i].Description, new { #class = "control-label col-md-2" })
#Html.CheckBoxFor(model => model.SelectedCustomerTypes[i].Selected, new { #checked = "checked" })
#Html.HiddenFor(model => model.SelectedCustomerTypes[i].Id, new { data_val = false })
#Html.HiddenFor(model => model.SelectedCustomerTypes[i].Description, new { data_val = false })
</div>
}
}
</div>
You need to fix the GetCustomerType method in your controller. Try this:
public IActionResult GetCostumerType(int? id)
{
if (id == null)
{
return NotFound();
}
var test = from custom in _yourContext.CustomerType
.Where(p => p.CustomerType.Id == customerId)
select custom;
//change _yourContext with your context variable
if (test == null)
{
return NotFound();
}
return View(test);
}
This way you are getting the custom types associated with the id passed in the method.
I have a simple select with an option selected from the model that is loaded.
To start here is my simple model
public class invoice
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public invoice_state invoice_state { get; set; }
}
public class invoice_state
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public string alias { get; set; }
public List<invoice> invoices { get; set; }
[Display(Name = "title")]
public string title { get; set; }
}
Here is what I have that works in one view.
in controller:
public IActionResult Create()
{
string state = "start_finish";
ViewBag.State = state;
var states = _context.invoice_state.Select(c => new {
id = c.ID,
title = c.title
}).ToList();
ViewBag.States = new SelectList(states, "id", "title", _context.invoice_state.Where(e => e.alias == state).FirstOrDefault().ID);
return View();
}
in the view
#Html.DropDownList("invoice_state", (SelectList)ViewBag.States, "--Select One--")
That works fine, the option is selected... but on my edit view which is set up mostly the same is not working.
in controller:
public async Task<IActionResult> Edit(int? id)
{
var invoice = await _context.invoice
.Include(_item => _item.invoice_state).SingleOrDefaultAsync(m => m.ID == id);
if (invoice == null)
{
return NotFound();
}
string state = "start_finish"; // as a default
var states = _context.invoice_state.Select(c => new { id = c.ID, title = c.title }).ToList();
if (null != invoice.invoice_state)
{
ViewBag.State = invoice.invoice_state.alias;
}
else
{
ViewBag.State = state;
}
ViewBag.States = new SelectList(states, "id", "title", _context.invoice_state.Where(e => e.alias == state).FirstOrDefault());
return View(invoice);
}
and in the edit view
#Html.DropDownList("invoice_state", (SelectList)ViewBag.States, "--Select One--")
I have read all over the place and can't find a simple answer that isn't wire up more files, and even those haven't helped get me to the need. I have tried to force it too and it is not working, but figured it was worth placing here too.
ViewBag.States = _context
.invoice_state
.Select(c => new SelectListItem {
Value = c.ID.ToString(),
Text = c.title
})
.Select(l => new SelectListItem {
Selected = (l.Value == invoice.invoice_state.ID.ToString()),
Text = l.Text,
Value = l.Value
});
but only get 'Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable<Microsoft.AspNetCore.Mvc.Rendering.SelectListItem>' to 'Microsoft.AspNetCore.Mvc.Rendering.SelectList' or the list version of the error if i add .ToList() on the select like before.
Some people have suggested to set the selected value, and as i read it it would be like,
if (null != invoice.invoice_state)
{
ViewBag.invoice_stateID = invoice.invoice_state.ID;
}
else
{
ViewBag.invoice_stateID = _context.invoice_state.Where(e => e.alias == "start_finish").FirstOrDefault().ID;
}
if i use
<select asp-for="invoice_state" asp-items="#ViewBag.States" >
<option>Please select one</option>
</select>
It doesn't work either, see the list but nothing selected. Last note, if I select it and submit it, it does set the value in the database, just when I get back to the edit it again fails to again select anything.
Also to be clear there is the data
which came from
<dt>
#Html.DisplayNameFor(model => model.invoice_state)
</dt>
<dd>
#Html.DisplayFor(model => model.invoice_state)
</dd>
#Stephen Muecke is right, the ticket is the ViewModel. Not a fan fully here but I'll simmer. The solution is not just getting it to show, you need to save is too. Here is the whole of it.
ViewModel
namespace project_name.Models.InvoiceViewModel
{
public class EditInvoiceViewModel
{
public int invoice_stateID { get; set; }
public invoice invoice { get; set; }
public List<SelectListItem> States { set; get; }
}
}
edit in action on controller
public async Task<IActionResult> Edit(int? id)
{
var invoice = await _context.invoice
.Include(_item => _item.invoice_state).SingleOrDefaultAsync(m => m.ID == id);
string state = "start_finish"; // as a default
var states = _context.invoice_state.Select(c => new { id = c.ID, title = c.title }).ToList();
if (null != invoice.invoice_state)
{
ViewBag.State = invoice.invoice_state.alias;
}
else
{
ViewBag.State = state;
}
var vm = new EditInvoiceViewModel();
vm.invoice = invoice;
vm.States = _context
.invoice_state
.Select(c => new SelectListItem
{
Value = c.ID.ToString(),
Text = c.title
})
.ToList();
if (null != invoice.invoice_state)
{
vm.invoice_stateID = invoice.invoice_state.ID;
} else {
vm.invoice_stateID = _context.invoice_state.Where(e => e.alias == "start_finish").FirstOrDefault().ID;
}
return View(vm);
}
to save in action on controller
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(EditInvoiceViewModel model)
{
if (ModelState.IsValid)
{
try
{
model.invoice.creator = await GetCurrentUserAsync();
model.invoice.invoice_state = await _context.invoice_state.SingleOrDefaultAsync(m => m.ID == model.invoice_stateID);
_context.Update(model.invoice);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!invoiceExists(model.invoice.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction("Index");
}
return View(model);
}
And in the view
<div class="form-group">
<label asp-for="#Model.invoice_stateID" class="col-md-2 control-label"></label>
<select asp-for="#Model.invoice_stateID" asp-items="#Model.States" >
<option>Please select one</option>
</select>
</div>
The thing I guess I was having a hard time with is all supporting questions that are like this, and all the blogs focus on the display, but i still need to save it, and that was not clear. I had to skip the [bind()] but I'll get back to that.
Below is the error i getting :
The model item passed into the dictionary is of type 'System.Collections.Generic.List[StoredProcedureEF_MVC.tbl_users]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable[StoredProcedureEF_MVC.Models.User]'.
I have tried a lot but don't know where i was doing wrong, no luck from last two days. I am learning MVC, so sorry if you got some stupid mistake.
My Model :
namespace StoredProcedureEF_MVC.Models
{
[Table("tbl_users")]
public class User
{
[Key]
public int UserId { get; set; }
[Required]
public string Username {get;set;}
[Required]
public string Email { get; set; }
}
}
VIEW:
#model IEnumerable<StoredProcedureEF_MVC.Models.User>
#{
ViewBag.Title = "Delete";
}
#{
var grid = new WebGrid(source: Model);
}
#grid.GetHtml(
columns: grid.Columns(
grid.Column("UserID"),
grid.Column("Username"),
grid.Column("Email"),
grid.Column(
format: (item) => Html.ActionLink("Delete", "DeleteUser", new {id=item.UserID })
)
)
)
CONTROLLER ACTION:
[HttpGet]
public ActionResult Delete()
{
SPEFMVCEntities conn = new SPEFMVCEntities();
var result = (from p in conn.tbl_users select p).ToList();
return View(result);
}
try
var result = conn.tbl_users.Select(c => new StoredProcedureEF_MVC.Models.User
{
UserId = c.UserId,
UserName = c.UserName,
Email = c.Email
}).ToList();
return View(result);
Try to change your controller method to this:
[HttpGet]
public ActionResult Delete()
{
SPEFMVCEntities conn = new SPEFMVCEntities();
var result = (from p in conn.tbl_users select p as StoredProcedureEF_MVC.Models.User).ToList();
return View(result);
}
I'm trying to get a dropdown show the right value when editing using a viewmodel but it only works when i pass the complete model to the view.
When I do it like this and there is already a contact selected it shows that contact in the edit screen.
Model
public class ClientModel
{
public int ID { get; set; }
public int ContactID { get; set; }
//Other atributes
}
View EditContact
#model Project.Models.ClientModel
#Html.DropDownListFor(model => model.ContactID , (SelectList)ViewBag.ContactID, "select a contact")
Controller
public ActionResult EditContact(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var Contact = db.Contacts.ToList();
ViewBagID.Contact = new SelectList(Contact.AsEnumerable(), "ID", "name", "Contact");
ClientModel model= db.ClientModel.Find(id);
return View(model);
}
But when I do it like this and there is already a contact selected the dropdownlist shows select contact.
Model
public class ClientModel
{
public int ID { get; set; }
public int ContactID { get; set; }
//Other atributes
}
ViewModel
public class ClientEditContactModel
{
public int ID { get; set; }
public int ContactID { get; set; }
}
View EditContact
#model Project.Models.ClientEditContactModel
#Html.DropDownListFor(model => model.ContactID, (SelectList)ViewBag.ContactID, "select a contact")
Controller
public ActionResult EditContact(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var Contact = db.Contacts.ToList();
ViewBag.ContactID = new SelectList(Contact.AsEnumerable(), "ID", "name", "Contact");
ClientModel client= db.ClientModel.Find(id);
ClientEditContactModel model = new ClientEditContactModel();
model.ID = client.ID;
model.ContactID = client.ContactID
return View(model);
}
How do i fix this with the viewmodel?
Edit
I've made some typo's in my code so I fixed them but because of them i found the answer see below.
I found the answer after some more research here https://stackoverflow.com/a/11949123/4252392.
The problem was that ViewBag's name is the same as the model's property.
So i changed the Viewbag's name.
New Controller
public ActionResult EditContact(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var Contact = db.Contacts.ToList();
ViewBag.ContactIDList = new SelectList(Contact.AsEnumerable(), "ID",
"name", "Contact");
ClientModel client= db.ClientModel.Find(id);
ClientEditContactModel model = new ClientEditContactModel();
model.ID = client.ID;
model.ContactID = client.ContactID
return View(model);
}
New View
#model Project.Models.ClientEditContactModel
#Html.DropDownListFor(model => model.ContactID, (SelectList)ViewBag.ContactIDList,
"select a contact")
If you set selected value in ContactID property from dropdown so you need to set dropdown in view like below:
#model Project.Models.ClientEditContactModel
#Html.DropDownListFor(model => model.ContactID, (SelectList)ViewBag.Contact,
"select a contact")