I have a page that show details of a post and Identified users can add commented on that post.
My problems:
PostID and UserID is FK in Comment model and don't pass from view to controller
CommnetMessage is Null!!
what is wrong?
Comment Model :
public class Comment : System.Object
{
public Comment()
{
this.CommnetDate = General.tzIran();
}
[Key]
public int CommentID { get; set; }
[Required]
public string CommnetMessage { get; set; }
[Required]
public DateTime CommnetDate { get; set; }
public string UserId { get; set; }
[Key, ForeignKey("UserId")]
public virtual ApplicationUser ApplicationUser { get; set; }
public int PostID { get; set; }
[Key, ForeignKey("PostID")]
public virtual Post posts { get; set; }
}
Post Model:
public class Post : System.Object
{
public Post()
{
this.PostDate = General.tzIran();
this.PostViews = 0;
}
[Key]
public int PostID { get; set; }
public string PostName { get; set; }
public string PostSummery { get; set; }
public string PostDesc { get; set; }
public string PostPic { get; set; }
public DateTime PostDate { get; set; }
public int PostViews { get; set; }
public string postMetaKeys { get; set; }
public string PostMetaDesc { get; set; }
public string UserId { get; set; }
[ForeignKey("UserId")]
public virtual ApplicationUser ApplicationUser { get; set; }
public int CategoryID { get; set; }
[ForeignKey("CategoryID")]
public virtual Category Category { get; set; }
public virtual ICollection<Comment> commnets {get; set;}
}
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
/*Realations*/
public virtual ICollection<Comment> Comments { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
View Model:
public class PostViewModel
{
public ApplicationUser Users { get; set; }
public Post posts { get; set; }
public Category Categories { get; set; }
public IEnumerable<Comment> ListCommnets { get; set; }
public Comment Commnets { get; set; }
}
Controller:
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var post = db.Posts.Find(id);
post.PostViews += 1;
db.SaveChanges();
if (post == null)
{
return HttpNotFound();
}
return View(new PostViewModel() { posts = post });
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Details([Bind(Include = "CommentID,CommnetMessage,CommnetDate,UserId,PostID")] Comment comment , int? id)
{
int pid = comment.PostID;
if (ModelState.IsValid)
{
db.CommentS.Add(comment);
db.SaveChanges();
TempData["notice"] = "پیغام شما با موفقیت ثبت شد.";
return RedirectToAction("success");
}
ViewBag.UserId = new SelectList(db.Users, "Id", "FirstName", comment.UserId);
ViewBag.PostID = id;
return View( new PostViewModel() { posts = db.Posts.Find(id)});
}
public ActionResult success()
{
ViewBag.Message = "از طریق فرم زیر می توانید برایمان پیغام بگذارید.";
return View("Details", new PostViewModel() { ListCommnets = db.CommentS });
}
Comment Partial View:
#using Microsoft.AspNet.Identity
#using FinalKaminet.Models
#using Microsoft.AspNet.Identity.EntityFramework
#model FinalKaminet.ViewModel.PostViewModel
#if (TempData["notice"] != null)
{
<p>#TempData["notice"]</p>
}
#if (Request.IsAuthenticated)
{
var manager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
var user = manager.FindById(User.Identity.GetUserId());
using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.posts.PostID)
#Html.HiddenFor(model => model.Users.Id)
<div class="form-group">
#Html.LabelFor(model => model.Users.FirstName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#{
var name = user.FirstName + " " + user.LastName;
}
<input type="text" id="Id" value="#name" disabled="disabled" class="form-control" />
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Commnets.CommnetMessage, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Commnets.CommnetMessage, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Commnets.CommnetMessage, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Send" class="btn btn-default" />
</div>
</div>
</div>
}
}
else
{
<p>#Html.ActionLink("Log in", "Login", "Account", new { returnUrl = Request.Url }, null)</p>
}
As #StephenMuecke stated, model of your view is PostViewModel and all editors, hidden fields are created based on your view model. For example, when you generate hidden field using #Html.HiddenFor(model => model.posts.PostID) and try to post your data MVC model binder tries to bind the value of this field to the model specified at your Action method. In your case it is Comment so , MVC model binder will try bind value of generated hidden field to Comment.posts.PostID which does not exist. To make everything work perfectly you have to use same view model as a argument of your action method:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Details(PostViewModel viewModel)
{
......
}
Also, again as #StephenMuecke sated, your view model should have only those properties which you need. For example, your PostViewModel should look like something as following:
public class PostViewModel
{
// Actually, you do not need UserId property
// as it should be retrieved inside controller
// from current user data
public string UserId { get; set; }
public string UserName { get; set; }
public int PostID { get; set; }
public string CommentMessage { get; set; }
}
Back to your action method, you have to map view model to your model:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Details(PostViewModel viewModel)
{
Comment comment = new Comment
{
CommnetMessage = viewModel.CommentMessage,
// and other properties
}
// Save your model and etc.
}
Related
I am using EF and my relationship between tables is many-to-many. I want to display and edit the ICollection property to not only display my ingredients from a recipe, but to also edit, add, or delete them.
I tried to use EditorFor, but the changes of the ingredient were never changed and submitted to the database. I want to use DropDownList because it can show the ingredients that my recipe has in a list format so I can choose between them.
This is my Recipe and Ingredients Model with the relational table RecipesIngredients:
namespace Licenta.Models
{
public class Recipe
{
[Key]
public int IDRecipe { get; set; }
public string Name { get; set; }
public string Desc { get; set; }
public string Steps { get; set; }
public float Kcal { get; set; }
public float Pro { get; set; }
public float Carbo { get; set; }
public float Fat { get; set; }
public virtual ICollection<RecipesIngredients> RecipesIngredients { get; set; }
}
}
namespace Licenta.Models
{
public class RecipesIngredients
{
[Key]
[Column(Order = 1)]
public int IDRecipe { get; set; }
[Key]
[Column(Order = 2)]
public int IDIngredient { get; set; }
public virtual Recipe Recipe { get; set; }
public virtual Ingredient Ingredient { get; set; }
}
}
namespace Licenta.Models
{
public class Ingredient
{
[Key]
public int IDIngredient { get; set; }
public string Nume { get; set; }
public float Kcal { get; set; }
public float Pro { get; set; }
public float Carbo { get; set; }
public float Fat { get; set; }
public virtual ICollection<RecipesIngredients> RecipesIngredients { get; set; }
}
}
This is my Controller:
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Recipe recipe = db.Recipes.Find(id);
if (recipe == null)
{
return HttpNotFound();
}
return View(recipe);
}
// POST: Recipes/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "IDRecipe,Name,Kcal,Pro,Carbo,Fat,Desc,Steps,Ingredients,RecipesIngredients")] Recipe recipe)
{
if (ModelState.IsValid)
{
db.Entry(recipe).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(recipe);
}
And the View of the Edit page:
#model Licenta.Models.Recipe
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Rețetă</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.IDRecipe)
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#foreach (var item in Model.RecipesIngredients)
{
<td>
#Html.DropDownListFor(model => item.Ingredient.Nume, #* this is where i want to edit the ingredients*#)
</td>
}
<div class="col-md-10">
#Html.ValidationMessageFor(model => model.RecipesIngredients, "", new { #class = "text-danger" })
</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>
}
That is the View that I want to use to edit my ingredients. Is there any way to do that?
I am trying to update supplier in database when users on webform insert data into textboxes.
First users in textbox for supplierID insert value, and on screen shows particular supplier from database. Then user can change supplier and when he is done he have to click on submit button.
I use EntityState.Modifier, but supplier doesn't change in database, and also I have no errors in the view. I think that's not working because my Supplier have foreign key from Adress table.
Does somebody know how to update using Entity state modified if a have a foreign key to another table?
I appreciate any help!
public partial class Supplier
{
public int SupplierID{ get; set; }
public string Name{ get; set; }
public string Phone{ get; set; }
public string Email { get; set; }
public Nullable<int> TownID{ get; set; }
public Nullable<int> StreetID{ get; set; }
public Nullable<int> AdressNumber{ get; set; }
public virtual Adress Adress { get; set; }
}
public partial class Town
{
public int TownID{ get; set; }
public string Name{ get; set; }
}
public partial class Street
{
public int TownID{ get; set; }
public int StreetID{ get; set; }
public string Name{ get; set; }
}
public partial class Adress
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Adress()
{
this.Supplier= new HashSet<Supplier>();
}
public int TownID{ get; set; }
public int StreetID{ get; set; }
public int AdressNumber{ get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Supplier> Suppliercs{ get; set; }
}
This is my View:
#model FpisNada.Models.Supplier
#{
ViewBag.Title = "Index";
Layout = null;
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.TextBoxFor(model => model.SupplierID, new { #placeholder = "pib dobavljaca", style = " float:left" })
<div class="col-md-9">
#if (ViewBag.ListTown!= null)
{
#Html.DropDownListFor(m => m.TownID, ViewBag.ListTown as SelectList, "--select town--", new { #class = "form-control", style = " float:left" })
}
#Html.DropDownListFor(m => m.StreetID, new SelectList(""), "--select street--", new { #class = "form-control", style = " float:left" })
<div class="container">
#Html.TextBoxFor(model => model.AdressNumber, new { #class = "form-control"})
#Html.TextBoxFor(model => model.Email, new { #class = "form-control" })
#Html.TextBoxFor(model => model.Name, new { #class = "form-control" })
#Html.TextBoxFor(model => model.Phone, new { #class = "form-control"})
</div>
</div>
<input type="submit" value="Edit" />
}
My controller method:
[HttpGet]
public ActionResult Edit(int id)
{
Supplier supplier= db.Supplier.Find(id);
return View(supplier);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit( Supplier supplier)
{
try
{
if (ModelState.IsValid)
{
db.Entry(supplier).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("ChangeSupplier");
}
}
catch (DataException /* dex */)
{
//Log the error (uncomment dex variable name after DataException and add a line here to write a log.)
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
}
return View(supplier);
}
I am very new to MVC and I am trying to create a cascading drop down. The user will select the name of the practice and the drop down below will populate with the names of the opticians who work at that practice.
Optician Model:
public class Optician
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid OpticianId { get; set; }
[ForeignKey("User")]
public string UserId { get; set; }
public virtual ApplicationUser User { get; set; }
public IEnumerable<SelectListItem> UserList { get; set; }
[ForeignKey("Practice")]
public Guid PracticeId { get; set; }
public virtual Practice Practice { get; set; }
public IEnumerable<SelectListItem> PracticeList { get; set; }
public virtual ICollection<ApplicationUser> Users { get; set; }
public virtual ICollection<Practice> Practices { get; set; }
}
Practice Model:
public class Practice
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Display(Name = "Practice")]
public Guid PracticeId { get; set; }
[Display(Name = "Practice Name")]
public string PracticeName { get; set; }
public virtual ICollection<Optician> Opticians { get; set; }
public virtual ICollection<Booking> Bookings { get; set; }
}
Application User Model:
public class ApplicationUser : IdentityUser
{
[Display(Name = "Title")]
public string Title { get; set; }
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
public string LastName { get; set; }
}
The Controller:
public ActionResult TestDropDown()
{
var practices = new SelectList(db.Practices, "PracticeId", "PracticeName");
ViewData["Practices"] = practices;
return View();
}
[HttpPost]
public JsonResult Opticians(Guid? Id)
{
var opticianList = db.Opticans.Where(a => a.PracticeId == Id).Select(a => a.User).ToList();
return Json(opticianList, JsonRequestBehavior.AllowGet);
}
The View:
<script src="~/Scripts/jquery-1.10.2.js"></script>
<script>
$(document).ready(function () {
$("#Optician").prop("disabled", true);
$("#Practice").change(function () {
$.ajax({
url : "#Url.Action("Opticians","Bookings1")",
type : "POST",
data : {Id : $(this).val() }
}).done(function(OpticianList){
$("#Optician").empty();
for (var i = 0; i < OpticianList.length; i++) {
$("#Optician").append("<option>" + OpticianList[i].FirstName + "</option>");
}
$("#Optician").prop("disabled", false);
});
});
});
</script>
#using (Html.BeginForm("TestDropDown", "Bookings1", FormMethod.Post))
{
#Html.AntiForgeryToken()
<h4>Select Practcie & Opticians</h4>
<hr />
#Html.ValidationSummary()
<div class="form-group">
#Html.Label("Select Practice :", new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.DropDownList("PracticeId", ViewData["Practices"] as SelectList, new { #class = "form-control" })
</div>
</div><br />
<div class="form-group">
#Html.Label("Select Optician :", new { #class = "col-md-2 control-label" })
<div class="col-md-10">
<select id="Optician"></select>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Submit" />
</div>
</div>
}
I can select the Name of the practice but the drop down for the Optician First Name does not populate.
Any help would be greatly appreciated
Thanks
You first <select> has id="PracticeId" but you script refers to an element with id="Practice" which does not exist, therefore is never run. Change you script to
var optician = $("#Optician"); // cache elements that you repeately refer to
optician.prop("disabled", true);
$("#PracticeId").change(function () { // change the selector
$.ajax({
url : "#Url.Action("Opticians","Bookings1")",
type : "POST",
data : {Id : $(this).val() }
}).done(function(OpticianList){
optician.empty();
for (var i = 0; i < OpticianList.length; i++) {
optician.append("<option>" + OpticianList[i].FirstName + "</option>");
}
optician.prop("disabled", false);
});
});
or you could just use the .getJSON() shortcut
$.getJSON('#Url.Action("Opticians","Bookings1")', { Id : $(this).val() }, function(OpticianList) {
// add the option elements
}
Since you only need the FirstName property of ApplicationUser, your controller code should be
var opticianList = db.Opticans.Where(a => a.PracticeId == Id).Select(a => a.User.FirstName)
and the script adjusted to
optician.append("<option>" + OpticianList[i] + "</option>");
or
optician.append($('<option></option>').text(OpticianList[i]));
so your not sending back a whole lot of extra data across the wire that you never use.
Try this in your action:
[HttpPost]
public JsonResult Opticians(Guid? Id)
{
var opticianList = db.Opticans.Where(a => a.PracticeId == Id).Select(a => a.User).ToList();
SelectList mySelectList = new SelectList(opticianList, "IDField", "DisplayField", 0);
return Json(mySelectList );
}
Here is the post I followed when I implemented this.
Fill drop down list on selection of another drop down list
I have the following entities:
public class Entidad
{
[Key]
public int Id { get; set; }
public string Nombre { get; set; }
public virtual ICollection<Propiedad> Propiedades { get; set; }
}
public class Propiedad
{
[Key]
public int Id { get; set; }
public virtual Entidad Entidad { get; set; }
public string Codigo { get; set; }
public string Nombre { get; set; }
public string TipoDeDatos { get; set; }
}
And I have this controller action
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include="Id,Codigo,Nombre,TipoDeDatos")] Propiedad propiedad)
{
if (ModelState.IsValid)
{
db.Propiedades.Add(propiedad);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(propiedad);
}
and on my view:
<div class="form-group">
#Html.LabelFor(model => model.Entidad, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(m => m.Entidad.Id, (SelectList)(ViewBag.EntidadList), "Seleccionar", new { #class = "form-control" })
</div>
</div>
However when I debug the controller the Entidad property on the Propiedad entity is NULL
http://screencast.com/t/CSgLzSCw
In the declaration on DataAnnotation Bind at the Create method, you are not including Entidad property, never will be binded, just remove the Bind DataAnnotation or include it.
For a school assignment I need to make a poll with ASP.NET
The problem I get when trying to write the answers in the database is that only one question and one answer gets written into it.
This is the View
#model CinemaJamV2.WebUIV2.Models.EnqueteModel
#{
ViewBag.Title = "Enquete";
}
<h2>Enquete</h2>
#Html.ValidationSummary(true)
#using (Html.BeginForm("Enquete", "Enquete", new { vraag = "vraag", antwoord = "antwoord", naam = "naam", cijfer = "cijfer" }))
{
<div class="col-md-12">
#for(var i=0;i< Model.enquetevragen.Count();i++)
{
<div class="thumbnail">
#Html.LabelFor(model => model.enquetevragen[i].vraag, new { htmlAttributes = new { #class = "form-control" } })
#Html.EditorFor(model => model.enquete.antwoord, new { htmlAttributes = new { #class = "form-control" } })
#Html.EditorFor(model => model.enquete.cijfer, new { htmlAttributes = new { #class = "form-control" } })
</div>
}
</div>
<div class="col-md-12">
<p>Naam <input type="text" name="naam" /> </p>
<input type="submit" name="submit" value="Verzend" />
</div>
}
This is the Controller:
namespace CinemaJamV2.WebUIV2.Controllers
{
public class EnqueteController : Controller
{
private IRepository<Enquete> repository;
private IRepository<EnqueteVraag> a_repository;
private CineJamContext db = new CineJamContext();
public EnqueteController(IRepository<Enquete> a_model, IRepository<EnqueteVraag> vraag_model)
{
repository = a_model;
a_repository = vraag_model;
}
[HttpGet]
public ActionResult Enquete()
{
EnqueteModel enquetevragen = new EnqueteModel
{
enquetevragen = a_repository.List
};
return View(enquetevragen);
}
[HttpPost]
public ActionResult Enquete(Enquete enquete)
{
if (ModelState.IsValid)
{
db.Enquetes.Add(enquete);
db.SaveChanges();
return RedirectToAction("Enquete");
}
return View(enquete);
}
}
}
The ModelView:
namespace CinemaJamV2.WebUIV2.Models
{
public class EnqueteModel
{
public List<Enquete> enquetes {get; set;}
public Enquete enquete { get; set; }
public List<EnqueteVraag> enquetevragen { get; set; }
}
}
And this is the Model Enquete which should contain all the given answers:
namespace CinemaJamV2.Domain.Entities
{
[Table("Enquete")]
public partial class Enquete : IEntity
{
public int Id { get; set; }
[StringLength(1000)]
public string vraag { get; set; }
[StringLength(1000)]
//[Required]
public string antwoord { get; set; }
public int? cijfer {get; set;}
[StringLength(50)]
//[Required]
public string naam { get; set; }
}
}
This Model contains all the Questions
namespace CinemaJamV2.Domain.Entities
{
[Table("EnqueteVraag")]
public partial class EnqueteVraag : IEntity
{
public int Id { get; set; }
[StringLength(1000)]
public string vraag { get; set; }
}
}
The action for POST has only one instance of the Model as its parameter. You need to read this: Model binding to a list
YOu need to use view model that will have list of Enquete and then in post method again you need to do for loop and save it to database.
See following links for samples.
http://www.binaryintellect.net/articles/b1e0b153-47f4-4b29-8583-958aa22d9284.aspx
http://www.c-sharpcorner.com/UploadFile/pmfawas/Asp-Net-mvc-how-to-post-a-collection/
http://www.codeproject.com/Tips/855577/List-of-Model-Object-Post-to-Controller-in-ASP-NET