Why viewmodel modelstate validates navigation property of an entity? - asp.net-mvc-4

I have a problem with simple validation during controller Create action for entity ARTICLE. I am using EF 4 database first. Entity ARTICLE is used as foreign key in entity ACTION(ACTION.ARTICLE_id). That's why code generation tool add navigation property to entity ARTICLE, even it does not make not much sense. Each time I update entities the clasess gets to form below(ARTICLE). I checked all foreign key many times again. I really dont know what to do with this error to make clean soluton, not just clearing error in controller action. Everything - even view are scaffolded.
Action:
[HttpPost]
[Authorize(Roles = "ARTICLE_ADMIN")]
public ActionResult Edit(ARTICLE article)
{
if (ModelState.IsValid)
{
article.date_modified = DateTime.Now;
string newimage = this.Request.Form["preview_image_filename"];
string oldimage = this.Request.Form["original_image_filename"];
if (newimage.NotNullOrEmpty())
{
article.preview_image = newimage;
}
else
{
article.preview_image = oldimage;
}
db.Entry(article).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
article.date_modified = DateTime.Now;
ViewBag.ARTICLE_CATEGORY_id = new SelectList(db.ARTICLE_CATEGORY, "ARTICLE_CATEGORY_id", "description", article.ARTICLE_CATEGORY_id);
ViewBag.ARTICLE_STATUS_id = new SelectList(db.ARTICLE_STATUS, "ARTICLE_STATUS_id", "description", article.ARTICLE_STATUS_id);
ViewBag.WEB_USER_id = new SelectList(db.WEB_USER, "WEB_USER_id", "login", article.WEB_USER_id);
return View(article);
}
I am using this entity model generated via code generation tool with added annotations in metadata class, it cant be more simple
public partial class ARTICLE
{
public ARTICLE()
{
this.PROGRAM_WEEK_DAY_ITEM = new HashSet<PROGRAM_WEEK_DAY_ITEM>();
this.STORAGE = new HashSet<STORAGE>();
this.SHOW = new HashSet<SHOW>();
this.ACTION = new HashSet<ACTION>();
}
public int ARTICLE_id { get; set; }
public System.DateTime date_created { get; set; }
public Nullable<System.DateTime> date_modified { get; set; }
public string title { get; set; }
public string html { get; set; }
public int WEB_USER_id { get; set; }
public int ARTICLE_STATUS_id { get; set; }
public int ARTICLE_CATEGORY_id { get; set; }
public Nullable<System.DateTime> date_published { get; set; }
public string preview_image { get; set; }
//code generation tool added those navigation props
public virtual ARTICLE_CATEGORY ARTICLE_CATEGORY { get; set; }
public virtual ARTICLE_STATUS ARTICLE_STATUS { get; set; }
public virtual WEB_USER WEB_USER { get; set; }
public virtual ICollection<PROGRAM_WEEK_DAY_ITEM> PROGRAM_WEEK_DAY_ITEM { get; set; }
public virtual ICollection<STORAGE> STORAGE { get; set; }
public virtual ICollection<SHOW> SHOW { get; set; }
//this one causes trouble I think, but no clue why
public virtual ICollection<ACTION> ACTION { get; set; }
}
metadata class - just display names and formats:
public class ARTICLE_Metadata
{
[Key]
public int ARTICLE_id { get; set; }
[Display(Name="Vytvořeno")]
public System.DateTime date_created { get; set; }
[Display(Name = "Změněno")]
public Nullable<System.DateTime> date_modified { get; set; }
[Display(Name = "Publikováno")]
[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}", ApplyFormatInEditMode = true)]
public Nullable<System.DateTime> date_published { get; set; }
[Display(Name = "Titulek článku")]
public string title { get; set; }
[Display(Name = "Obsah článku")]
[UIHint("tinymce_full"), AllowHtml]
public string html { get; set; }
[Display(Name = "Vytvořil")]
public int WEB_USER_id { get; set; }
[Display(Name = "Status")]
public int ARTICLE_STATUS_id { get; set; }
[Display(Name = "Kategorie")]
public int ARTICLE_CATEGORY_id { get; set; }
[Display(Name = "Náhledový obrázek")]
public string preview_image { get; set; }
}
and finally form in razor view:
#using (Html.BeginForm("Create", "Articles", FormMethod.Post, new { #class = "base-form" }))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Nový článek</legend>
#Html.DatePickerFor(model => model.date_published, false)
#Html.HiddenFor(model => model.WEB_USER_id)
<p class="editor-label">
#Html.LabelFor(model => model.ARTICLE_STATUS_id)
#Html.DropDownList("ARTICLE_STATUS_id")
#Html.ValidationMessageFor(model => model.ARTICLE_STATUS_id)
</p>
<p class="editor-label">
#Html.LabelFor(model => model.ARTICLE_CATEGORY_id)
#Html.DropDownList("ARTICLE_CATEGORY_id")
#Html.ValidationMessageFor(model => model.ARTICLE_CATEGORY_id)
</p>
<p class="editor-label">
#Html.LabelFor(model => model.title)
#Html.EditorFor(model => model.title)
#Html.ValidationMessageFor(model => model.title)
</p>
<div class="html-editor">
#Html.EditorFor(model => model.html)
</div>
<p>
<input type="submit" value="Vytvořit" class="submit" />
</p>
</fieldset>
}
When model validates and comes to controller action, ModelState.IsValid == false, ModelState claims error on property ACTION which is not even present in the table and not supposed to be there, it is navigation property.
Error has an exeption: The parameter conversion from type 'System.String' to type 'namespace.ACTION' failed because no type converter can convert between these types.
I tried to attach debugger view image but this web did not allowed it to me. I have other entities managed via controllers and view the same way - about 30 where this does not happen.
How can I get rid of this problem without creating extra model with the same properties but without navigation ones? Or just prevent this navigation property to be included to validation. Or it is a new microsoft nonsense feature?

Some times these weird errors in db-first comes from that the name of a navigation property in an entity, is same as the name of another entity. I myself experienced these problems sometimes, and I don't know exactly what is the reason.
Anyway, renaming that navigation property must get you rid of that weird error...

Related

Model evaluating to false while saving to database

So I have this problem when trying to save an item to the database in my asp.net mvc4 web app.
I have three classes listed below
public class Posts
{
[Key]
public int PostID { get; set; }
[Required(ErrorMessage="A Title is required for your Post")]
[Display(Name="Title")]
public string PostTitle { get; set; }
[Required(ErrorMessage="This Field is Required")]
[Display(Name = "Post")]
public string PostContent { get; set; }
[Required()]
[DataType(DataType.DateTime)]
public DateTime PostDate { get; set; }
//public int AuthorID { get; set; }
//public int CommentID { get; set; }
[NotMapped]
public virtual List<Comments> Comment { get; set; }
public virtual Users user { get; set; }
}
and this class has a many to one relationship with the users class below
public class Users
{
public Users()
{
}
[Key]
public int UserID { get; set; }
[Required()]
public string Firstname { get; set; }
[Required()]
public string Lastname { get; set; }
[Required()]
[DataType(DataType.Date, ErrorMessage="Enter a Valid Date")]
public DateTime DOB { get; set; }
//[Required()]
//public string Photo { get; set; }
[Required()]
public string Sex { get; set; }
[Required()]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
public DateTime RegDate { get; set; }
[Required()]
[Column("Username")]
//[Remote("doesUserNameExist", "Account", HttpMethod = "POST", ErrorMessage = "User name has already been taken. Please enter a different User name.")]
public string Username { get; set; }
[Required()]
[DataType(DataType.Password)]
public string Password { get; set; }
public string PasswordSalt { get; set; }
[System.ComponentModel.DataAnnotations.Compare("Password", ErrorMessage="Passwords do not match")]
[DataType(DataType.Password)]
//[NotMapped]
//public string ConfirmPassword { get; set; }
public virtual List<Posts> Post { get; set; }
public virtual List<Comments> Comment { get; set; }
}
The database table for the Posts.cs class has a field called user_UserID which i assume is to store the id of the user that creates a post. I'm trying to save the posts to the database using the below code
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Posts</legend>
<div class="form-group">
#Html.LabelFor(model => model.PostTitle)
#Html.TextBoxFor(model => model.PostTitle, new {#class = "form-control"})
#Html.ValidationMessageFor(model => model.PostTitle)
</div>
<div class="form-group">
#Html.LabelFor(model => model.PostContent)
#Html.TextAreaFor(model => model.PostContent, new {#class = "form-control"})
#Html.ValidationMessageFor(model => model.PostContent)
</div>
<input type="hidden" name="user.UserID" value="#Session["LoggedUserID"]" />
<div>
<input type="submit" value="Submit" />
</div>
</fieldset>
}
As you can see, the user ID i'm saving to the database table is gotten from the user ID stored in the Session["LoggedUserID"] variable. The controller that handles the saving is below
public class PostsController : Controller
{
//
// GET: /Posts/
BlogContext db = new BlogContext();
public ActionResult Index()
{
return View();
}
[HttpGet]
public ActionResult Create()
{
return View();
}
[HttpPost]
public ActionResult Create(Posts Post)
{
var errors = ModelState.Values.SelectMany(v => v.Errors);
try
{
if (ModelState.IsValid)
{
var newPost = db.Post.Create();
newPost.PostTitle = Post.PostTitle;
newPost.PostContent = Post.PostContent;
newPost.PostDate = DateTime.Now;
newPost.user.UserID = Post.user.UserID;
db.Post.Add(newPost);
db.SaveChanges();
return RedirectToAction("Index", "Posts");
}
else
{
ModelState.AddModelError("", "Something is wrong with the model");
}
}
catch (NullReferenceException ex)
{
Debug.WriteLine("Processor Usage" + ex.Message);
}
return View();
}
}
I attached a breakpoint to the Modelstate.IsValid line and then debugged, but i noticed the ModelState is always evaluating to false and the ModelState.AddModelError is showing that is there is something wrong with validating the model. Ive tried all possible tweakings all to no avail. I need help. How can i save the Posts to the database table without this problem.
Please what am i doing wrong?
I suspect ModelState is invalid because you are posting a value for user.UserId (the hidden input) which is initializing the property User which is invalid because of the validation attributes applied to other properties of User. It would be better to create a view model for creating new Posts that contain only the properties you need to display/edit. It is not necessary to include a property for User (the author of the post) since this can be set in the controller when you save the data (similar to what you are doing for the PostDate property
View model
public class PostVM
{
[Required(ErrorMessage="A Title is required for your Post")]
[Display(Name="Title")]
public string Title { get; set; }
[Required(ErrorMessage="This Field is Required")]
[Display(Name = "Post")]
public string Content { get; set; }
}
Controller
[HttpGet]
public ActionResult Create()
{
PostVM model = new PostVM();
return View(model);
}
[HttpPost]
public ActionResult Create(PostVM model)
{
if(!ModelState.IsValid)
{
return View(model);
}
// Initialize new Posts
// Map properties from the view model, set the user and date
// Save and redirect
}
View
#model PostVM
#using (Html.BeginForm()) {
....
<div class="form-group">
#Html.LabelFor(model => model.Title)
#Html.TextBoxFor(model => model.Title, new {#class = "form-control"})
#Html.ValidationMessageFor(model => model.Title)
</div>
<div class="form-group">
#Html.LabelFor(model => model.Content)
#Html.TextAreaFor(model => model.Content, new {#class = "form-control"})
#Html.ValidationMessageFor(model => model.Content)
</div>
<input type="submit" value="Submit" />
}

Dropdown List MVC 4 error

I am trying to get a drop down list to work but its not working for me. This application is mainly a festival based application where you can add a festival along with your events. The error I am getting is on line:
#Html.DropDownList("towns", (IEnumerable<SelectListItem>)ViewData["Town"], new{#class = "form-control", #style="width:250px" })
This is the error I get:
There is no ViewData item of type 'IEnumerable' that has the key 'towns'.
Create.cshtml
<div class="form-group">
#Html.LabelFor(model => model.FestivalTown, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("towns", (IEnumerable<SelectListItem>)ViewData["Town"], new{#class = "form-control", #style="width:250px" })
#Html.ValidationMessageFor(model => model.FestivalTown)
</div>
#*#Html.Partial("ddlFestivalCounty");*#
</div>
Controller.cshtml
//Get
List<SelectListItem> Towns = new List<SelectListItem>();
Towns.Add(new SelectListItem { Text = "Please select your Town", Value = "SelectTown" });
var towns = (from t in db.Towns select t).ToArray();
for (int i = 0; i < towns.Length; i++)
{
Towns.Add(new SelectListItem
{
Text = towns[i].Name,
Value = towns[i].Name.ToString(),
Selected = (towns[i].ID == 0)
});
}
ViewData["Town"] = Towns;
//Post
festival.FestivalTown.Town = collection["Town"];
Model.cs
public class Festival
{
public int FestivalId { get; set; }
[Required]
[Display(Name = "Festival Name"), StringLength(100)]
public string FestivalName { get; set; }
[Required]
[Display(Name = "Start Date"), DataType(DataType.Date)]
public DateTime StartDate { get; set; }
[Required]
[Display(Name = "End Date"), DataType(DataType.Date)]
public DateTime EndDate { get; set; }
[Required]
[Display(Name = "County")]
public virtual County FestivalCounty { get; set; }
[Display(Name = "Festival Location")]
public DbGeography Location { get; set; }
[Required]
[Display(Name = "Town")]
public virtual Town FestivalTown { get; set; }
[Required]
[Display(Name = "Festival Type")]
public virtual FestivalType FType { get; set; }
public UserProfile UserId { get; set; }
}
public class Town
{
public int ID { get; set; }
[Display(Name = "Town")]
public string Name { get; set; }
}
I suspect that this error occurs when you submit the form to the [HttpPost] action and not when you are rendering the form, right? And this action renders the same view containing the dropdown, right? And inside this [HttpPost] action you forgot to populate the ViewData["Town"] value the same way you did in your HttpGet action, right?
So, go ahead and populate this property the same way you did in your GET action. When you submit the form to your [HttpPost] action, only the selected value is sent to the controller. So you need to repopulate the collection values if you intend to redisplay the same view, because this view renders a dropdown which is attempting to bind its values from ViewData["Town"].
And here's what I mean in terms of code:
[HttpPost]
public ActionResult SomeAction(Festival model)
{
... bla bla bla
// don't forget to repopulate the ViewData["Town"] value the same way you did in your GET action
// if you intend to redisplay the same view, otherwise the dropdown has no way of getting
// its values
ViewData["Town"] = ... same stuff as in your GET action
return View(model);
}
And all this being said, I would more than strongly recommend you using view models instead of this ViewData/ViewBag weakly typed stuff. Not only that your code will become much more clean, but even the error messages will start making sense.

System.Collections.Generic.IEnumerable<MyBlogSite.Models.BlogPost>' does not contain a definition for 'Categories'

Here is my BlogPost Model class:
public class BlogPost
{
public int Id { get; set; }
public string Title { get; set; }
[AllowHtml]
public string ShortDescription { get; set; }
[AllowHtml]
public string PostBody { get; set; }
public string Meta { get; set; }
public string UrlSlug { get; set; }
public DateTime PostedOn { get; set; }
public DateTime? Modified { get; set; }
public virtual ICollection<BlogPostCategory> Categories { get; set; }
public virtual ICollection<BlogPostTag> Tags { get; set; }
}
Here is my BlogPostCategory Model class:
public class BlogPostCategory
{
public int Id { get; set; }
public string Name { get; set; }
public string UrlSlug { get; set; }
public string Description { get; set; }
// Decared virtual because the data must be returned from another table.
public virtual ICollection<BlogPost> BlogPosts { get; set; }
}
Each class belongs to a separate Controller/View.
Finally, here is the top port of the Index View for Blog:
#model IEnumerable<MyBlogSite.Models.BlogPost>
#{
ViewBag.Title = "Index";
}
#Html.RenderPartial("~/Views/Category/_Categories.cshtml", Model.Categories );
<p>
#Html.ActionLink("New Blog Post", "Create")
</p>
<table>
<tr>
<th>
#Html.DisplayNameFor(model => model.Title)
</th>
....
In the View where Model.Categories is being passed in is where I am getting the exception from the title of this post. It seems to me that I have defined Categories within the BlogPost Model. What am I doing wrong?
The Model on your Razor page is an IEnumerable<MyBlogSite.Models.BlogPost>. It seems you're trying to display the information about each of the items in your collection. If so, then you can loop through them or create a display/editor template and use #Html.DisplayFor(x => x) or #Html.EditorFor(x => x), respectively.
#foreach(var post in Model) {
<p>Do stuff here with the local "post" variable.</p>
}
Here's a link to Scott Gu's blog talking about the #model directive in Razor views.

generate dropdownlist from a table in database

I'm tryng to be more precise to my previous question which can be found here, I got some nice answers but couldn't figure out how to use it in my situation Previous question
I got some nice answers but couldn't figure out how to use it in my situation.
basically I want to have registration page which contains
Email //Comes from my AspNetUser(datamodel) class, also AspNetUsers table exists in database.
UserName//Comes from my AspNetUser(datamodel) class, also AspNetUsers table exists in database.
Password//Comes from my AspNetUser(datamodel) class, also AspNetUsers table exists in database.
Role//dropdownlist, comes from Role(datamodel) class, also Roles table exists in database
In my controller I have impelmented my Register method in following way:
public class AccountController : Controller
{
//private readonly IDbContext dbContext;
//
// GET: /Account/
[HttpGet]
public ActionResult Login()
{
return View();
}
[HttpPost]
[AllowAnonymous]
public ActionResult Login(LoginModel model)
{
if(Membership.ValidateUser(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
return RedirectToAction("Index", "Home");
}
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
[HttpGet]
public ActionResult Register()
{
string [] roles = Roles.GetAllRoles();
return View(roles);
}
[HttpPost]
public ActionResult Register(AspNetUser model)
{
return View();
}
}
in my get method i'm passing the roles to view and right now i'm using AspNetUser as model in View
#model Sorama.CustomAuthentiaction.Models.AspNetUser
#{
ViewBag.Title = "Register";
Layout = "~/Views/shared/_BootstrapLayout.empty.cshtml";
}
#section Styles{
<link href="#Url.Content("~/Content/bootstrap.css")" rel="stylesheet" type="text/css" />
}
<div class ="form-signin">
#using (Html.BeginForm("Login", "Account"))
{
#Html.ValidationSummary(true)
<h2 class="form-signin-heading"> Register </h2>
<div class ="input-block-level">#Html.TextBoxFor(model=>model.Email, new{#placeholder = "Email"})</div>
<div class ="input-block-level">#Html.TextBoxFor(model=>model.UserName, new{#placeholder = "UserName"})</div>
<div class ="input-block-level">#Html.PasswordFor(model=>model.Password, new{#placeholder ="Password"})</div>
<div class ="input-block-level">#Html.DropdownlistFor(.....//don't no how to generate dropdownlist)
<button class="btn btn-large btn-primary" type="submit">Sign In</button>
}
</div>
can u tell me how to get that dropdownlist and how can I pass that selected value to controller to use it so that i can put user in role during registration? Would it be better to create new model for Registration?
Edit: AspNetUser model
public class AspNetUser
{
private ICollection<Role> _roles= new Collection<Role>();
public Guid Id { get; set; }
[Required]
public virtual String Username { get; set; }
public virtual String Email { get; set; }
[Required, DataType(DataType.Password)]
public virtual String Password { get; set; }
public virtual String FirstName { get; set; }
public virtual String LastName { get; set; }
[DataType(DataType.MultilineText)]
public virtual String Comment { get; set; }
public virtual Boolean IsApproved { get; set; }
public virtual int PasswordFailuresSinceLastSuccess { get; set; }
public virtual DateTime? LastPasswordFailureDate { get; set; }
public virtual DateTime? LastActivityDate { get; set; }
public virtual DateTime? LastLockoutDate { get; set; }
public virtual DateTime? LastLoginDate { get; set; }
public virtual String ConfirmationToken { get; set; }
public virtual DateTime? CreateDate { get; set; }
public virtual Boolean IsLockedOut { get; set; }
public virtual DateTime? LastPasswordChangedDate { get; set; }
public virtual String PasswordVerificationToken { get; set; }
public virtual DateTime? PasswordVerificationTokenExpirationDate { get; set; }
public virtual ICollection<Role> Roles
{
get { return _roles; }
set { _roles = value; }
}
}
You'd better have a view model specifically designed for this view. Think of what information you need in the view and define your view model:
public class RegisterViewModel
{
public string Email { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string SelectedRole { get; set; }
public IEnumerable<SelectListItem> Roles { get; set; }
}
As you can see from this view model, in order to have a dropdown list you need 2 properties: one scalar property that will hold the selected value and one collection property to hold the list of available values.
and then:
public ActionResult Register()
{
string [] roles = Roles.GetAllRoles();
var model = new RegisterViewModel();
model.Roles = roles.Select(r => new SelectListItem
{
Value = r,
Text = r,
});
return View(model);
}
[HttpPost]
public ActionResult Register(RegisterViewModel model)
{
// the model.SelectedRole will contain the selected value from the dropdown
// here you could perform the necessary operations in order to create your user
// based on the information stored in the view model that is passed
// NOTE: the model.Roles property will always be null because in HTML,
// a <select> element is only sending the selected value and not the entire list.
// So if you intend to redisplay the same view here instead of redirecting
// makes sure you populate this Roles collection property the same way we did
// in the GET action
return Content("Thanks for registering");
}
and finally the corresponding view:
#model RegisterViewModel
#{
ViewBag.Title = "Register";
Layout = "~/Views/shared/_BootstrapLayout.empty.cshtml";
}
#section Styles{
<link href="#Url.Content("~/Content/bootstrap.css")" rel="stylesheet" type="text/css" />
}
<div class ="form-signin">
#using (Html.BeginForm("Login", "Account"))
{
#Html.ValidationSummary(true)
<h2 class="form-signin-heading"> Register </h2>
<div class ="input-block-level">
#Html.TextBoxFor(model => model.Email, new { placeholder = "Email" })
</div>
<div class ="input-block-level">
#Html.TextBoxFor(model => model.UserName, new { placeholder = "UserName" })
</div>
<div class ="input-block-level">
#Html.PasswordFor(model => model.Password, new { placeholder = "Password" })
</div>
<div class ="input-block-level">
#Html.DropdownlistFor(model => model.SelectedRole, Model.Roles)
</div>
<button class="btn btn-large btn-primary" type="submit">Sign In</button>
}
</div>

MVC submit multiple objects in model when form post

I am facing a problem that when I have a complex Model, if I submit the form it will not give me all the values of all the model properties, in the below example, I am not getting back the gridModel properties:
Model
public class InventoryModel {
public GridModel GridModel { get; set; }
public Int32 UserKey { get; set; }
}
public class GridModel {
public String GridId { get; set; }
public String GridName { get; set; }
public List<String> columns { get; set; }
}
Controller
public ActionResult Index(){
InventoryModel model = new InventoryModel();
model.UserKey= 20014;
model.GridModel = new GridModel();
model.GridModel.GridId = "jqgInventory";
model.GridModel.GridName = "Inventory Grid";
return View(model);
}
[HttpPost]
public ActionResult Index(InventoryModel model){
Int32 userId = model.UserKey; // This has a value
String gridId = model.GridModel.GridId; // This doesn't have a value
String gridName= model.GridModel.GridName; // This doesn't have a value
}
View
#model InventoryModel
#using (Html.BeginForm()) {
#Html.TextBoxFor(m => m.UserKey, new { #class = "w200" })
#Html.TextBoxFor(m => m.GridModel.GridId , new { #class = "w200" })
#Html.TextBoxFor(m => m.GridModel.GridName, new { #class = "w200" })
<input type="submit" value="Submit" />
}
Any suggestion please would be appreciated.
Thanks,
Alaa
You could instead use a ViewModel rather than the actual Model. This would be a flatter class that reflects the data specifically for the View.
public class InventoryViewModel{
Int32 UserKey {get; set; }
public String GridId { get; set; }
public String GridName { get; set; }
}
Your controller can map your model to your ViewModel if necessary