Every time I get ModelState.isvalid = false. and the employee.ID is null. On the page-html I use asp-for in a hidden so it should match against id, but still comes out null, anyone have an idea?
public Employees Employees {get; set;}
public async Task<IActionResult> OnPost()
{
//if (!ModelState.IsValid)
//{
// var errors = ModelState.SelectMany(x => x.Value.Errors.Select(z => z.Exception));
//}
if (ModelState.IsValid)
{
var dbEmpy = await _db.Employees.FindAsync(Employees.ID);
dbEmpy.FirstName = Employees.FirstName;
dbEmpy.LastName = Employees.LastName;
dbEmpy.Salary = Employees.Salary;
dbEmpy.isCEO = Employees.isCEO;
dbEmpy.isManager = Employees.isManager;
dbEmpy.ManagerId = Employees.ManagerId;
await _db.SaveChangesAsync();
return RedirectToPage("ListEmpolyee/index");
}
return RedirectToPage();
}
}
and page
<div class="border container" style="padding:20px;">
<form method="post">
<input type="hidden" asp-for="Employees.ID" />
<span class="text-danger" asp-validation-summary="ModelOnly"></span>
<div class="form-group row">
<div class="col-4">
<label asp-for="Employees.FirstName"></label>
</div>
</div>
public class Employees
{
[Key]
public int ID { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public decimal Salary { get; set; }
public bool isCEO { get; set; }
public bool isManager { get; set; }
public int? ManagerId { get; set; }
}
Related
I'm working on a small library website where you should be able to post a comment to each individual book. The problem is in my view that I can't say "Asp-for='BookComment.Name'" since my BookComment is a list in my Book Model
My Book Model
public class Book
{
[Key]
public int BookID { get; set; }
[Required]
[Column(TypeName = "Varchar(75)")]
public string Title { get; set; }
[Required]
[Column(TypeName = "Varchar(75)")]
public string Author { get; set; }
[Required]
[Column(TypeName = "Varchar(13)")]
public string Isbn { get; set; }
[Required]
[Column(TypeName = "Varchar(50)")]
public string Publisher { get; set; }
public int Sites { get; set; }
public DateTime ReleaseDate { get; set; }
public string Summary { get; set; }
public string Picture { get; set; }
public DateTime AddedDate { get; set; }
public int Stars { get; set; }
public List<BookCategory> BookCategory { get; set; } = new List<BookCategory>();
public List<BookComment> BookComment { get; set; } = new List<BookComment>();
}
BookComment Model:
public class BookComment
{
[Key]
public int BookCommentID { get; set; }
[Required]
public int BookID { get; set; }
[Column(TypeName = "Varchar(50)")]
public string Name { get; set; }
[Column(TypeName = "Varchar(100)")]
public string Email { get; set; }
public string Review { get; set; }
public DateTime Date { get; set; }
public decimal Stars { get; set; }
}
My Book Controller
public class BookController : Controller
{
private readonly ApplicationDbContext _db;
public BookController(ApplicationDbContext db)
{
_db = db;
}
public IActionResult Book(int? id)
{
var book = _db.Books.Include(o => o.BookComment).FirstOrDefault(p => p.BookID == id);
return View(book);
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Book(BookComment comment)
{
_db.bookComments.Add(comment);
_db.SaveChanges();
return RedirectToAction("Book");
}
}
Snippet of my form
#model LibraryNew.Models.Book
<h5 class="mt-4">Tilføj en anmeldelse</h5>
<p>Din email vil ikke blive offentliggjort</p>
<form asp-action="Book" method="post"></form>
<div class="my-3">
<div class="form-group">
<label for="exampleFormControlSelect1">Antal stjerner:</label>
<select class="form-control col-md-1" id="exampleFormControlSelect1">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="1">5</option>
</select>
</div>
</div>
<div class="form-group">
<label for="exampleFormControlInput1">Email address</label>
<input type="email" class="form-control" id="exampleFormControlInput1" placeholder="name#example.com">
</div>
<div class="form-group">
<label for="exampleFormControlInput1">Email address</label>
<input type="email" class="form-control" id="exampleFormControlInput1" placeholder="name#example.com">
</div>
<div class="form-group">
<label for="exampleFormControlTextarea1">Example textarea</label>
<textarea class="form-control" id="exampleFormControlTextarea1" rows="3"></textarea>
</div>
I can't say <select asp-for="BookComment.Stars"
Any help is appreciated. If any further information is needed please let me know!
In your view code try replacing #model LibraryNew.Models.Book with #model LibraryNew.Models.BookComment so you can have access to it in asp-for since what you are trying to do is post a single BookComment to add it to a book in your controller.
It looks like what is needed is to be able to use different models in a single page to achieve this you can create a single class that contains the models you will need for a single page for example
public class BookLibrary
{
public Book Book { get; set; }
public BookComment BookComment { get; set; }
public Author Author{ get; set; }
}
then in your view code you use #model LibraryNew.Models.BookLibrary and in your asp-for you will be able to access BookComment by using Model.BookComment
I have some problem.
I have next model:
public class DocumentViewModel
{
public string Nazvanie { get; set; }
public Author DocumentAutors { get; set; }
}
public class Author
{
public long Id { get; set; }
public List<IPerson> Authors { get; set; }
}
public interface IPerson
{
long Id { get; set; }
}
public class PersonUL : IPerson
{
public long Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}
public class PersonIP : IPerson
{
public long Id { get; set; }
public string FirstName { get; set; }
public string SecondNAme { get; set; }
public string PostAddress { get; set; }
}
In .cshtml
#model DocumentViewModel
#if (Model.DocumentAutors.Authors != null && Model.DocumentAutors.Authors.Count > 0)
{
for (int i = 0; i < Model.DocumentAutors.Authors.Count; i++)
{
if (Model.DocumentAutors.Authors is PersonUL )
{
<div class="form-group">
<label asp-for="#Model.DocumentAutors.Authors[i].Name" class="col-md-10 control-label"></label>
<div class="col-md-10">
<input asp-for="#Model.DocumentAutors.Authors[i].Name" class="form-control" />
<span asp-validation-for="#Model.DocumentAutors.Authors[i].Name" class="text-danger"></span>
</div>
</div>
}
}
}
Model.DocumentAutors.Authors[i] don't contain "Name" field, because it's interface. I need cast it, but if i write
if (Model.DocumentAutors.Authors is PersonUL )
{
PersonUL ul = (PersonUL)Model.DocumentAutors.Authors[i];
<div class="form-group">
<label asp-for="#ul.Name" class="col-md-10 control-label"></label>
<div class="col-md-10">
<input asp-for="#ul.Name" class="form-control" />
<span asp-validation-for="#ul.Name" class="text-danger"></span>
</div>
</div>
}
i will get html with wrong name like this
<input class="form-control" type="text" id="Name" name="Name" value="566">
instead
<input class="form-control" type="text" id="DocumentAutors.Authors[0].Name" name="DocumentAutors.Authors[0].Name" value="566">
and ModelBinder will not bint this field into Authors List.
Is there a solution for this problem or should I make one generic model for PersonUL and PersonIP with all fields, which I don't really like it?
I am sorry but IMHO I don't see any advantages in the interface. It only makes the code more confused.
Why you don't try
public class Author
{
public long Id { get; set; }
public List<PersonIP> IpAuthors { get; set; }
public List<PersonUL> UlAuthors { get; set; }
}
or even better
public class DocumentViewModel
{
public string Nazvanie { get; set; }
public long AuthorId { get; set; }
public List<PersonIP> IpAuthors { get; set; }
public List<PersonUL> UlAuthors { get; set; }
}
I have a Model CargoType with id , name and few others. When adding new cargo type to database all of them are required, but when i'm adding new object of Cargo which have amount and cargoType for which i use <select> to choose from existing ones. Id is what is really choosen in this select but then because CargoType.Name is null, ModelState.isValid is false.
Cargo model:
public class Cargo
{
public int Id { get; set; }
public int Amount { get; set; }
[Required]
public CargoType CargoType { get; set; }
}
CargoType model:
public class CargoType
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public int Height { get; set; } //in millimeters
public int Width { get; set; } //in millimeters
public int Length { get; set; } //in millimeters
public bool StackingAllowed { get; set; }
}
Part of the View:
Order have List<Cargo> Cargo
#model TransportationManagementSystem.Models.Order
[...]
#for (int i = 0; i < 2; i++)
{
<div class="form-group">
<label asp-for="Cargo[i].Amount" class="control-label"></label>
<input asp-for="Cargo[i].Amount" class="form-control" />
<span asp-validation-for="Cargo[i].Amount" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Cargo[i].CargoType" class="control-label"></label>
<select asp-for="Cargo[i].CargoType.Id" class="form-control" asp-items="ViewBag.CargoTypes"></select>
<span asp-validation-for="Cargo[i].CargoType" class="text-danger"></span>
</div>
}
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,PickUpAddress,DropOffAddress,PickUpTime,DropOffTime,OrderState,Cargo")] Order order)
{
if (order.Cargo != null)
{
foreach (var cargo in order.Cargo)
{
var cargoType = await _context.CargoTypes.FindAsync(cargo.CargoType.Id);
cargo.CargoType = cargoType;
}
}
//ModelState.Clear();
if (ModelState.IsValid)
{
_context.Add(order);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(order);
}
How should i go about this?
I'm trying to make a form that i could save a file(image) , but it shows me an error:
InvalidOperationException: The property 'Product.Image' is of an interface type ('IFormFile'). If it is a navigation property manually configure the relationship for this property by casting it to a mapped entity type, otherwise ignore the property from the model.
Apply
I dont know how to fix it , here's the code:
Product.cs
public class Product
{
public Product()
{
OrderDetails = new HashSet<OrderDetails>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int? CategoryId { get; set; }
public decimal? Price { get; set; }
public int? Quantity { get; set; }
public string ImagePath { get; set; }
public virtual ICollection<OrderDetails> OrderDetails { get; set; }
public virtual Category Category { get; set; }
}
ProductFormViewModel.cs
public class ProductFormViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int? CategoryId { get; set; }
public decimal? Price { get; set; }
public int? Quantity { get; set; }
public IFormFile Image { get; set; }
}
Create Action
[HttpGet]
public IActionResult Create()
{
var categories = _repository.GetCategories().ToList();
var categoriesModel = categories.Select(p => new
{
p.Id,
p.Name
});
ViewBag.Categories = new SelectList(categoriesModel, "Id", "Name");
return View();
}
[HttpPost]
public IActionResult Create(ProductFormViewModel product)
{
var file = product.Image; // **it returns NULL**
var upload = Path.Combine(_environment.ContentRootPath, "wwwroot\\uploads", product.Name);
if (!Directory.Exists(upload))
Directory.CreateDirectory(upload);
var filePath = Path.Combine(upload, file.FileName);
if (file.Length > 0)
{
using (var fileStream = new FileStream(filePath, FileMode.Create))
{
file.CopyTo(fileStream);
}
}
var producti = new Product();
producti.CategoryId = product.CategoryId;
producti.Description = product.Description;
producti.Name = product.Name;
producti.Price = product.Price;
producti.Quantity = product.Quantity;
producti.ImagePath = filePath;
_repository.AddProduct(producti);
_repository.SaveChanges();
return RedirectToAction("Index","Products");
}
Create.cshtml
#model ProductFormViewModel
<br />
<br />
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
</div>
<div class="panel-body">
<form class="form-group" asp-action="Create" asp-controller="Products" method="post">
<input type="hidden" asp-for="Id"/>
<div class="col-md-12">
<div class="form-group col-md-6">
<label asp-for="Name" class="control-label col-md-3"></label>
<input asp-for="Name" type="text" class="form-control col-md-3"/>
</div>
<div class="form-group col-md-6">
<label asp-for="CategoryId" class="control-label col-md-3"></label>
<select asp-for="CategoryId" asp-items="#ViewBag.Categories" class="form-control col-md-3">
<option hidden disabled selected >Select One</option>
</select>
</div>
<div class="form-group col-md-6">
<label asp-for="Description" class="control-label col-md-3"></label>
<textarea asp-for="Description" class="form-control" rows="4"></textarea>
</div>
<div class="form-group col-md-6">
<label asp-for="Price" class="control-label col-md-3"></label>
<input type="text" asp-for="Price" class="form-control col-md-3"/>
</div>
<div class="form-group col-md-6">
<label asp-for="Quantity" class="control-label col-md-3"></label>
<input type="text" asp-for="Quantity" class="form-control col-md-3"/>
</div>
<div class="form-group col-md-12">
<label class="control-label">Select Image</label>
<input asp-for="Image" type="file" class="btn-file"/>
</div>
<div class="form-group col-md-12 text-center">
<input type="submit" class="btn btn-success" value="Save"/>
</div>
</div>
</form>
</div>
</div>
</div>
IFormFile is a type used by the ASP.NET Core framework and it does not have a sql server type equivalent.
For your domain model store it as byte[] and when you work with views, is ok for you to use the IFormFile type.
ProductModel:
public class Product
{
public Product()
{
OrderDetails = new HashSet<OrderDetails>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int? CategoryId { get; set; }
public decimal? Price { get; set; }
public int? Quantity { get; set; }
public string ImagePath { get; set; }
public virtual ICollection<OrderDetails> OrderDetails { get; set; }
public virtual Category Category { get; set; }
}
ProductViewModel:
public class ProductViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int? CategoryId { get; set; }
public decimal? Price { get; set; }
public int? Quantity { get; set; }
public IFormFile Image { get; set; }
}
Controller method:
[HttpGet]
public IActionResult Create()
{
var categories = _repository.GetCategories().ToList();
var categoriesModel = categories.Select(p => new
{
p.Id,
p.Name
});
ViewBag.Categories = new SelectList(categoriesModel, "Id", "Name");
return View();
}
[HttpPost]
public IActionResult Create(ProductViewModel model)
{
// Save the image to desired location and retrieve the path
// string ImagePath = ...
// Add to db
_repository.Add(new Product
{
Id = model.Id,
ImagePath = ImagePath,
// and so on
});
return View();
}
Also specify to the form enctype="multipart/form-data" in your view.
using System.ComponentModel.DataAnnotations.Schema;
namespace model{
public class ProductViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int? CategoryId { get; set; }
public decimal? Price { get; set; }
public int? Quantity { get; set; }
[NotMapped]
public IFormFile Image { get; set; }
}
}
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.
}