"Controller, Action, Model" not all code paths return a value - asp.net-mvc

I am really confused by this error "not all code paths return a value" on my action PostResponse. I have stared at my model, controller and view for hours and I think I have all paths covered. Of course the project won't build, so I can't debug further.
My action
// POST: /Questions/ViewQuestion/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult PostResponse([Bind(Include = "UserId,QuestionID,Answer,Source,Status,DateStamp")] Response response)
{
if (ModelState.IsValid)
{
db.Responses.Add(response);
db.SaveChanges();
}
}
My view
#model Template.Models.Question
#using Microsoft.AspNet.Identity
#{
ViewBag.Title = "View question";
var qtype = Model.QuestionTypeId;
ViewBag.Number = Model.Id - 7;
}
#using (Html.BeginForm("Question", "ViewQuestion", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
<div>
<h4>Question ##ViewBag.Number</h4>
<hr />
<h1> #Model.Question1</h1>
</div>
<div class="form-group">
#switch (qtype)
{
case 1:
// Textbox
#Html.TextArea("Answer", new { #class = "form-control", rows = "4", col = "5" });
break;
case 2:
// Dropdown
<select class="form-control" id="Answer">
#foreach (var item in Model.QuestionOptions.OrderBy(o => o.QuestionOptionRanking))
{
<option value="#item.QuestionOption1">#item.QuestionOption1</option>
}
</select>
break;
case 3:
// Checkbox
<div class="checkbox">
#foreach (var item in Model.QuestionOptions.OrderBy(o => o.QuestionOptionRanking))
{
<input type="checkbox" name="Answer" value="#item.QuestionOption1" /> #item.QuestionOption1 <br />
}
</div>
break;
case 4:
// Radio buttons
foreach (var item in Model.QuestionOptions.OrderBy(o => o.QuestionOptionRanking))
{
<div class="radio">
<label>
<input type="radio" name="Answer" value="#item.QuestionOption1" />
#item.QuestionOption1
</label>
</div>
}
break;
}
</div>
#using Template.Models.Response
#Html.HiddenFor(r => r.Responses, new { UserId = User.Identity.GetUserId(), Source = "Web", Status = "New", DateStamp = System.DateTime.Now })
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Answer" />
</div>
</div>
<br />
<hr />
<p>
#Html.ActionLink("Previous", "ViewQuestion", new { id = Model.Id - 1 }) |
#Html.ActionLink("Next", "ViewQuestion", new { id = Model.Id + 1 })
</p>
The page displays perfectly, but I can't test the post action as I cannot build with the current error.

Worked it out; but it was almost from scratch, as I created a new ViewModel and used that to populate the responses.
[HttpPost]
public ActionResult ViewQuestion([Bind(Include = "QuestionId, Answer, UserId")] ResponseViewModel responseViewModel)
{
Response re = new Models.Response();
re.Answer = responseViewModel.Answer;
re.UserId = responseViewModel.UserId;
re.QuestionId = responseViewModel.QuestionId;
re.DateStamp = System.DateTime.Now;
db.Responses.Add(re);
db.SaveChanges();
return RedirectToAction("ViewQuestion");
}
Thanks for your input as your comments got the old head working again. Thanks!

Your PostResponse action, or method specifies an ActionResult as a return type, but does not actually return anything. You can resolve this by changing it from ActionResult to void

try
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult PostResponse([Bind(Include = "UserId,QuestionID,Answer,Source,Status,DateStamp")] Response response)
{
if (ModelState.IsValid)
{
db.Responses.Add(response);
db.SaveChanges();
}
else{
return View("Error");
}
}

Related

How to update multiple models at the same time in ASP.NET MVC 5 Entity Framework

I am trying to update Two models at the same time.
Models:
Page
Fields
One page has multiple fields, I want them to update at the same time.
public class PageEditViewModel
{
public Page mPage { get; set; }
public IEnumerable<Field> Fields { get; set; }
}
Here is my View:
<div class="row">
<div class="col-md-12">
<h3>Fields</h3>
#foreach (var field in Model.ContentFields)
{
#Html.HiddenFor(m => field.Id)
switch (field.FieldType)
{
case "TextBox":
<div class="form-group">
<label class="control-label">
#field.FieldName<span class="required"> * </span>
</label>
#Html.TextBoxFor(m => field.Content , new { #class = "form-control" })
</div>
break;
case "TextArea":
<div class="form-group">
<label class="control-label">
#field.FieldName<span class="required"> * </span>
</label>
#Html.TextAreaFor(m => field.Content, new { #class = "form-control" })
</div>
break;
case "Image":
<div class="form-group">
<label class="control-label">
#field.FieldName<span class="required"> * </span>
</label>
<input type="file" name="contentImage" id="cImage" class="form-control" accept="image/*" />
</div>
break;
}
}
</div>
</div>
And Controller:
public ActionResult Update(PageEditViewModel viewModel)
{
if (!ModelState.IsValid)
{
var page = _context.MenuPages.Single(s => s.Id == viewModel.mPage.Id);
var contentFields = _context.ContentFields.Where(c => c.MenuPageId == page.Id);
var viewM = new PageEditViewModel
{
DashboardHeading = "Edit a Page",
mPage = page,
ContentFields = contentFields
};
return View("EditPage", viewM);
}
var pageEdit = _context.MenuPages.SingleOrDefault(p => p.Id == viewModel.mPage.Id);
pageEdit.Name = viewModel.mPage.Name;
pageEdit.IsActive = viewModel.mPage.IsActive;
pageEdit.IsShowInMenu = viewModel.mPage.IsShowInMenu;
// _context.SaveChanges();
foreach (var field in viewModel.ContentFields)
{
var cfield = _context.ContentFields.SingleOrDefault(f => f.Id == field.Id);
cfield.Content = field.Content;
}
_context.SaveChanges();
When I Send the data from View to Controller, I get the data for Pages but Null for the Fields Model (Object Reference not set to an instance...).
I am looking forward to any guide from members here.
Thanks.
I think Umesh answer is correct.
After changing your loop, how are you setting your Html.TextBoxFor, HiddenFor and so on?
It should be:
#Html.HiddenFor(m => m.ContentFields[i].Id)
You need to replace #foreach loop on view
#foreach (var field in Model.ContentFields)
{
By for loop
#for (var i = 0; i < Model.ContentFields.Count; i++)
{
This will bind your list to model, while sending data to controller

MVC Error on Displaying Data From Web API

I am new to ASP.NET programming and I encounter some problem, maybe someone can help me to find the solution.
I followed and completed the procedure here BeginForm with IEnumerable. But when I tried to run my codes. Something error happens on browser. The error is:
The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[ek_oms.Models.AddCategoryViewModel]', but this dictionary requires a model item of type 'ek_oms.Models.AddCategoryViewModel'.
Here's my related codes to that part.
Class:
public class AddCategoryViewModel
{
public IEnumerable<CategoryViewModel> CurrentCategories { get; set; }
public CategoryModel NewCategory { get; set; }
}
Partial codes in View:
#model ek_oms.Models.AddCategoryViewModel
#if (Model.CurrentCategories != null)
{
foreach (var item in Model.CurrentCategories)
{
<tr>
<td>#item.CategoryName</td>
<td>
#using (Html.BeginForm("CategoryDelete", "Admin", new { catID = item.Id }, FormMethod.Post, null))
{
#Html.AntiForgeryToken()
#Html.ActionLink("Edit", "CategoryEdit", null, new { catID = item.Id }, new { #class = "btn btn-primary btn-xs" })
<button type="submit" class="btn btn-danger btn-xs" onclick="return confirm('Are you sure you want to delete record with Category = #item.CategoryName')">Delete</button>}
</td>
</tr>
}
}
<div class="tab-content">
#using (Html.BeginForm("Categories", "Admin", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { role = "form" }))
{
#Html.AntiForgeryToken()
<div class="box box-primary">
<!-- /.box-header -->
<div class="box-body">
<div class="box-header">
<h3 class="box-title">Add New Category</h3>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
#Html.EditorFor(model => model.NewCategory.CategoryName, new { htmlAttributes = new { #class = "form-control", id = "Category", #placeholder = #Html.DisplayNameFor(model => model.NewCategory.CategoryName) } })
#Html.ValidationMessageFor(model => model.NewCategory.CategoryName, "", new { #class = "text-danger" })
</div>
<div class="form-group">
</div>
</div>
</div>
</div>
<div class="box-footer">
<button type="submit" class="btn btn-primary" id="submitAddCatBtn">Add</button>
</div>
</div>
}
</div>
Controller:
public async Task<ActionResult> Categories()
{
ViewBag.Message = TempData["Message"];
ViewBag.CatUsedMessage = TempData["CatUsedMessage"];
HttpResponseMessage responseMessage = await client.GetAsync(urlProducts + "/categories");
if (responseMessage.IsSuccessStatusCode)
{
var responseData = responseMessage.Content.ReadAsStringAsync().Result;
var categories = JsonConvert.DeserializeObject<List<AddCategoryViewModel>>(responseData);
return View(categories);
}
return View("Error");
}
Can someone help me to this. I don't know the right term to start searching. Thanks.
var categories = JsonConvert.DeserializeObject<List<AddCategoryViewModel>>(responseData);
Above line is the issue.
Your view is expecting just one AddCategoryViewModel object and not a List<AddCategoryViewModel>. As the error message clearly suggests.
So, change your code to return only one object if you are expecting just one from the api as below:
var categories = JsonConvert.DeserializeObject<AddCategoryViewModel>(responseData);
OR if you need more than one category
Change your #model in view as below:
#model List<ek_oms.Models.AddCategoryViewModel>
In this case you will need to change your view code to handle Category list accordingly as below:
#foreach(var cat in Model)
{
if (cat != null)//use cat in place of Model in your view
{
foreach (var item in cat.CurrentCategories)
{
}
}
}

MVC multiple select List not showing default values

Been working on this issue for a few hours now, maybe I'm missing something simple here, but no matter what I try I can't get the default selected items to work.
The controller function:
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Room room = db.Rooms.Find(id);
if (room == null)
{
return HttpNotFound();
}
List<int> allowedMods = new List<int> {1, 2};
List<keyval> allMods = new List<keyval>
{
new keyval(1,"A"),
new keyval(2,"B"),
new keyval(3,"C")
};
MultiSelectList multiList = new MultiSelectList(allMods, "ID", "Name", allowedMods);
ViewBag.mods = multiList;
return View(room);
}
Simple helper class keyval:
public class keyval
{
public int ID { get; set; }
public string Name { get; set; }
public keyval() { }
public keyval(int ID, string Name)
{
this.ID = ID;
this.Name = Name;
}
}
The view:
#model X.Models.Room
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Room</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.ID)
<div class="form-group">
#Html.Label("Moderators Allowed", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.ListBox("mods", ViewBag.mods as MultiSelectList, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Relevant generated Html:
<div class="col-md-10">
<select class="form-control" id="mods" multiple="multiple" name="mods">
<option value="1">A</option>
<option value="2">B</option>
<option value="3">C</option>
</select>
</div>
I've tried so many different variations and when I submit the form I do get the new list of selected items, However the default values is not working for me.
I would really appreciate help on this issue.
The name of your listbox is the same as the name of your ViewBag property that holds the list, there is a strange bug with ViewBag that causes things to not render properly if this is the case. Try changing ViewBag.mods to ViewBag.moderators or something other than ViewBag.mods.
Tested using your code and that corrects the problem for me.

View is passed a list then how do I update on the post?

In my GET I have:
return View(listOfEntries);
But then when I try to update in my Post the Entry object is all 0's.
Basically my view contains all entries for that particular id. It is displaying fine but ultimately I want to be able to edit any of the existing Entry fields or add a new one.
Right now when I go into my POST
public ActionResult Edit([Bind(Include =
"EntryId,UserId,EntryQuestionId,EntryQuestion,EntryQuestionVotes,EntryAnswer,EntryReviews,
QuestionValidationURL,TopicId")] Entry entry)
The entry object has no values or 0's. I'm guessing it is because the model was a list of entry objects but I'm not sure how I can accomplish what I'm trying to do.
My View:
#model List<projInterview.Models.Entry>
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm())
{
var iLoopCount = 0;
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Entry</h4>
<hr />
#Html.ValidationSummary(true)
#foreach (var item in Model)
{
#Html.HiddenFor(model => item.EntryId)
<div class="form-group">
#if(iLoopCount == 0)
{
#Html.LabelFor(model => item.EntryQuestion, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DisplayFor(model => item.EntryQuestion)
</div>
}
else
{
<hr/>
}
</div>
<div class="form-group">
#Html.LabelFor(model => item.EntryAnswer, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DisplayFor(model => item.EntryAnswer)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => item.EntryReviews, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#{
var iCount = 0;
var inputName = "EntryReviews" + item.EntryId;
for (var i = 1; i <= 5; i++)
{
iCount = i;
if (item.EntryReviews == i)
{
<input name=#inputName type="radio" class="star" checked="checked" value="#i" />
}
else
{
<input name=#inputName type="radio" class="star" value="#i" />
}
}
}
</div>
#*#Html.Partial("_ReviewPartial")*#
</div>
iLoopCount++;
#*if (iLoopCount >= Model.Count())
{
<hr/>
<div class="form-group">
#Html.LabelFor(model => item.EntryAnswer,"Add New Answer: ", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextArea("EntryAnswer",String.Empty)
#Html.ValidationMessageFor(model => item.EntryAnswer)
</div>
</div>
}*#
}
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
My Controller (partial):
// GET: /Entry/Edit/5
[Authorize]
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Entry entry = db.Entries.Find(id);
List<Entry> listOfEntries = new List<Entry>();
listOfEntries = db.Entries.Where(e => e.EntryQuestionId == id).ToList();
if (listOfEntries == null)
{
return HttpNotFound();
}
ViewBag.TopicId = new SelectList(db.Topics, "TopicId", "TopicName", entry.TopicId);
return View(listOfEntries);
}
// POST: /Entry/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize]
public ActionResult Edit([Bind(Include = "EntryId,UserId,EntryQuestionId,EntryQuestion,EntryQuestionVotes,EntryAnswer,EntryReviews,QuestionValidationURL,TopicId")] Entry entry)
{
if (ModelState.IsValid)
{
EntryAnswerReview myReview = new EntryAnswerReview();
myReview.EntryId = entry.EntryId;
myReview.EntryAnswerReviewValue = entry.EntryReviews;
myReview.DateReviewed = DateTime.Now;
myReview.UserId = User.Identity.GetUserId();
db.Reviews.Add(myReview);
List<Int32> reviews = new List<Int32>();
reviews = db.Reviews.Where(x => x.EntryId == entry.EntryId).Select(x => x.EntryAnswerReviewValue).ToList();
Double average;
if (reviews.Count == 0)
{
average = entry.EntryReviews;
}
else
{
average = reviews.Average();
}
entry.EntryReviews = Convert.ToInt32(average);
entry.UserId = User.Identity.GetUserId();
if (entry.UserId == null)
{
throw new InvalidOperationException("User [" +
User.Identity.Name + " ] not found.");
}
entry.LastUpdateDate = DateTime.Now;
db.Entry(entry).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.TopicId = new SelectList(db.Topics, "TopicId", "TopicName", entry.TopicId);
return View(entry);
}
Further Clarification of what I want to do:
Lets say the Edit View on the get returns 2 Entry items:
Entry #1
Entry #2
I want to be able to update either one of those from this view.
But I also want to have a "New Entry" option (Right now just a text area for the answer. So in my controller if that is filled out I would like to be able to capture that a new entry is present and add it to the Database.

How to display a model state error in case I am returning a partial view

I have the following action method for creating new network info:-
public ActionResult CreateVMNetwork(int vmid)
{
VMAssignIps vmips = new VMAssignIps()
{
TechnologyIP = new TechnologyIP() { TechnologyID = vmid},
IsTMSIPUnique = true,
IsTMSMACUnique = true
};
return PartialView("_CreateVMNetwork",vmips);
}
[HttpPost]
public ActionResult CreateVMNetwork(VMAssignIps vmip)
{
if (ModelState.IsValid)
{
try
{
repository.InsertOrUpdateVMIPs(vmip.TechnologyIP,User.Identity.Name);
repository.Save();
return PartialView("_networkrow",vmip);
}
catch (Exception ex)
{
ModelState.AddModelError(string.Empty, "Error occurred: " + ex.InnerException.Message);
}
}
return PartialView("_CreateVMNetwork", vmip);
}
And I have the following _CreateVMNetwork view:-
#model TMS.ViewModels.VMAssignIps
#using (Ajax.BeginForm("CreateVMNetwork", "VirtualMachine", new AjaxOptions
{
InsertionMode = InsertionMode.InsertAfter,
UpdateTargetId = "networktable",
LoadingElementId = "loadingimag",
HttpMethod= "POST"
}))
{
#Html.ValidationSummary(true)
#Html.HiddenFor(model=>model.TechnologyIP.TechnologyID)
#Html.Partial("_CreateOrEditVMNetwork", Model)
<input type="submit" value="Save" class="btn btn-primary"/>
}
and _CreateOrEditVMNetwork view:-
#model TMS.ViewModels.VMAssignIps
<div>
<span class="f">IP Address</span>
#Html.EditorFor(model => model.TechnologyIP.IPAddress)
#Html.ValidationMessageFor(model => model.TechnologyIP.IPAddress)
<input type="CheckBox" name="IsTMSIPUnique" value="true" #(Html.Raw(Model.IsTMSMACUnique ? "checked=\"checked\"" : "")) /> |
<span class="f"> MAC Address</span>
#Html.EditorFor(model => model.TechnologyIP.MACAddress)
#Html.ValidationMessageFor(model => model.TechnologyIP.MACAddress)
<input type="CheckBox" name="IsTMSMACUnique" value="true" #(Html.Raw(Model.IsTMSMACUnique ? "checked=\"checked\"" : "")) />
</div>
The problem I am facing is that in case there is a model state error when adding a new entity, a partial view will be displayed with the model state error as follow:-
So my question is , if there is a way to display the model state error with the partial view , without updating the table row “insert after” as I am doing currently?
Thanks
Given the age i'm guessing you have already found a solution to this,
But here is an example using InsertionMode.Replace, maybe it can help someone else.
Snipped from view
#using (Ajax.BeginForm("AddPerson", "Home", new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.Replace, UpdateTargetId = "UpdateSection" }))
{
<div id="UpdateSection">
#Html.Partial("PersonModel", Model.Person)
</div>
<input type="submit" value="add" />
}
Snipped from the controller
if (!ModelState.IsValid)
{
return PartialView("AddPerson", Person);
}
just make sure the "jquery.unobtrusive-ajax.min.js" script is included (i'm not sure it is by default)

Resources