Below is a portion of my controller:
[Authorize]
public ActionResult Edit(string IdAffaire)
{
Affaire affaire = this.repository.Retrieve(IdAffaire);
if (affaire == null)
{
return Redirect("~/");
}
var model = new AffaireEditViewModel
{
Affaire = affaire,
Status = repository.RetrieveStatus().Select(o => new SelectListItem { Text = o.Name, Value = o.IdStatus.ToString() }).ToList(),
};
return View(model);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(string idAffaire, AffaireEditViewModel model)
{
Affaire affaire = repository.Retrieve(idAffaire);
if (!ModelState.IsValid)
{
return this.Edit(model.Affaire.IdAffaire);
}
try
{
UpdateModel(affaire);
repository.Save();
return RedirectToAction("Detail", "Affaire", new { idAffaire = idAffaire });
}
catch
{
return View(affaire);
}
}
Below is my ViewModel for edit:
public class AffaireEditViewModel
{
public Affaire Affaire { get; set; }
public IEnumerable<SelectListItem> Status { get; set; }
}
Below is my Affaire model:
public class Affaire
{
[Key]
public string IdAffaire { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Username { get; set; }
public Int16? IdStatus { get; set; }
public Int16? IdLabel { get; set; }
// ....
}
My problem is that when posting new values in my edit view page, the action named Edit is well triggered (posting) with right values, but the statement UpdateModel(affaire) has no effect! Any help is greatly appreciated.
EDITED
I found the problem.
I need to change from this:
UpdateModel(affaire);
To this:
UpdateModel(affaire,"Affaire");
I guess it is because my view model is composed of several things and I need to tell explicitly to my UpdateModel function which element to use. Can somebody confirm?
To verify - is your repository retaining a reference to that instance of the model? I see you call save - but I don't see the implementation of the save since you aren't passing in a model to it.
Related
I have the following code in the controller and showing exception.
[HttpGet("{id}")]
public IActionResult GetCategoryGoalsById(int id)
{
try
{
var categories = _unitOfWork.Category.GetCategoryByGoalId(id);
if (categories == null)
{
_loggerManager.LogError($"Category with id: {id}, hasn't been found in db.");
return NotFound();
}
else
{
_loggerManager.LogInfo($"Returned category with id: {id}");
var categoryResult = _mapper.Map<CategoryDetailVm>(categories);
return Ok(categoryResult);
}
}
catch (Exception ex)
{
_loggerManager.LogError($"Something went wrong inside categoryResult action: {ex.Message}");
return StatusCode(500, "Internal server error");
}
}
Where is the entity class is like this:
public class Category
{
public int Id { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
public string CategoryName { get; set; }
[ForeignKey(nameof(Goals))]
public int GoalId { get; set; }
public Goals Goals { get; set; }
}
and vm class for the module class written as:
public class CategoryDetailVm
{
public int Id { get; set; }
public string CategoryName { get; set; }
}
The code is written in repository pattern with UnitofWork and the repository part is written as:
public IEnumerable<Category> GetCategoryByGoalId(int goalId)
{
return FindByCondition(g => g.Goals.Id.Equals(goalId)).ToList();
}
following exception is showing here, how can I resolve the following problem:
ex {"Missing type map configuration or unsupported mapping.\r\n\r\nMapping types:\r\nObject ->
CategoryDetailVm\r\nSystem.Object ->
EthosAPI.ViewModelEntities.CategoryDetailVm"} System.Exception
{AutoMapper.AutoMapperMappingException}
It seems like you're missing an automapper mapping, did you add it?
https://docs.automapper.org/en/stable/Getting-started.html#how-do-i-use-automapper
eg. var config = new MapperConfiguration(cfg => cfg.CreateMap<CategoryDetailVm, Categorie>());
Also you're mapping an object to an entire list, so you should also have a mapping for lists, see:
https://docs.automapper.org/en/stable/Lists-and-arrays.html
So var categoryResult = _mapper.Map<CategoryDetailVm>(categories); should be more like var categoryResult = _mapper.Map<IEnumerable<CategoryDetailVm>>(categories); or something.
I have two models:
public class UserInfo
{
public long ID { get; set; }
[Required]
[StringLength(50)]
public string FirstName { get; set; }
//...
public bool Falg{ get; set; }
}
public class UserInfoExtra
{
public long ID { get; set; }
[Required]
public string PhoneNumber { get; set; }
//...
}
Those two models are combined in a ViewModel:
public class UserViewModel
{
public UserInfo UserInfo { get; set; }
public UserInfoExtra ExtraInfo { get; set; }
}
In the controller:
public ActionResult Create(UserViewModel userinfo)
{
if(userInfo.Flag){
//Remove ExtaInfo from validation in ModelState.IsValid?
}
if (ModelState.IsValid)
{
db.UserInfos.Add(userinfo);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(userinfo);
}
How can I remove the ExtraInfo from validation in ModelState.IsValid?
Removing errors from ModelState is not a good idea. If you don't want the UserInfoExtra class to be validated when using the UserViewModel, then you should define your View Model differently (like below), and then use conditional validation on the PhoneNumber property:
public class UserViewModel
{
public UserInfo UserInfo { get; set; }
public long ID { get; set; }
//[RequiredIf] (conditional validation here)...
public string PhoneNumber { get; set;}
}
Here you can find an implementation of RequiredIf attribute: RequiredIf Conditional Validation Attribute
Ignore other properties(other than UserInfo) : ModelState.IsValidField(UserInfo)
Clear/Remove property error : ModelState["ExtraInfo"].Errors.Clear();
Create custom validator, as also suggested by ataravati : MVC Custom validation attribute
Here is an extension method that you can write to group all the modelstate errors as a Dictionary collection and now you can remove an item using it's key.
public static IEnumerable Errors(this ModelStateDictionary modelState)
{
if (!modelState.IsValid)
{
return modelState.ToDictionary(kvp => kvp.Key,
kvp => kvp.Value.Errors
.Select(e => e.ErrorMessage).ToArray())
.Where(m => m.Value.Count() > 0);
}
return null;
}
and you can access the modelstate error collection by var errors = ModelState.Errors();
You can try :
public ActionResult Create([Bind(Exclude="ExtraInfo")]UserViewModel userinfo)
{
...
}
Hope will help.
Could somebody please provide an example of how to combine two models within one view?
Currently I have a page called RecordCard which contains:
#model IEnumerable<WebApplication1.Models.Weight>
This is provided by the following code in the AccountController:
public ActionResult RecordCard()
{
var UserId = User.Identity.GetUserId();
var weightModel = from m in db.Weights where m.UserId == UserId select m;
return View(weightModel);
}
The RecordCard page also contains a form which is bound to the following class:
public class AddWeightModel
{
[Required]
[DataType(DataType.Text)]
[Display(Name = "Stone")]
public Nullable<short> Stone { get; set; }
[Required]
[DataType(DataType.Text)]
[Display(Name = "Pound")]
public Nullable<short> Pound { get; set; }
}
However, these are two individual models with different purposes, so how do I combine to a single model that contains an IEnumerable list and set of form elements that will ultimately post to the AccountController correctly to add a record to the database using the following code:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult RecordCard(Weight Model)
{
if (ModelState.IsValid)
{
using (WebApplication1Entities db = new WebApplication1Entities())
{
Weight weight = new Weight();
weight.UserId = User.Identity.GetUserId();
weight.Stone = Model.Stone;
weight.Pound = Model.Pound;
weight.Date = System.DateTime.Now;
db.Weights.Add(Model);
db.SaveChanges();
}
}
return View(Model);
}
I have included the Weight class below:
public partial class Weight
{
public int Id { get; set; }
public string UserId { get; set; }
public Nullable<short> Stone { get; set; }
public Nullable<short> Pound { get; set; }
public Nullable<System.DateTime> Date { get; set; }
}
Also here is the WebApplication1Entities class which declares the Weight table as Weights:
public partial class WebApplication1Entities : DbContext
{
public WebApplication1Entities()
: base("name=WebApplication1Entities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<Weight> Weights { get; set; }
}
Please explain what needs to be modified and how, no matter what I try to read, follow and implement, I seem to be missing something.
Any help would be much appreciated :-)
I would say this is good example of using ViewModel here. I would suggest something like -
Create ViewModel with the composition of the two classes
public class AddWeightModel
{
[Required]
[DataType(DataType.Text)]
[Display(Name = "Stone")]
public Nullable<short> Stone { get; set; }
[Required]
[DataType(DataType.Text)]
[Display(Name = "Pound")]
public Nullable<short> Pound { get; set; }
}
....
public partial class Weight
{
public int Id { get; set; }
public string UserId { get; set; }
public Nullable<short> Stone { get; set; }
public Nullable<short> Pound { get; set; }
public Nullable<System.DateTime> Date { get; set; }
}
.....
public class WeightViewModel
{
public IList<AddWeightModel> AddWeightModel { get; set; }
public Weight Weight { get; set; }
}
Then change your view to accept the view models -
#model WeightViewModel
Finally modify your controller to cope with the change -
public ActionResult RecordCard()
{
var UserId = User.Identity.GetUserId();
var weightModel = from m in db.Weights where m.UserId == UserId select m;
var viewModel = new WeightViewModel
{
Weight = weightModel,
AddWeightModel = new List<AddWeightModel>(){}
};
return View(viewModel);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult RecordCard(WeightViewModel viewModel)
{
Weight Model = viewModel.Weight;
if (ModelState.IsValid)
{
using (WebApplication1Entities db = new WebApplication1Entities())
{
Weight weight = new Weight();
weight.UserId = User.Identity.GetUserId();
weight.Stone = Model.Stone;
weight.Pound = Model.Pound;
weight.Date = System.DateTime.Now;
db.Weights.Add(Model);
db.SaveChanges();
}
}
return RedirectToAction("RecordCard");
}
I've tackled this before, can came to an elegant solution.
First, you'd want to setup your main classes to send, as well as a 'holder' class to store them to eventually send to a view.
As you probably found out, this is because a view can't have multiple models sent to it.
public class WebsiteTheme
{
public string Color { get;set; }
public string Title { get;set; }
public WebsiteTheme() {
Color = "blue";
Title = "test website";
}
}
public class User
{
public string Name { get;set; }
public string Gender { get;set; }
public User() {
Name = "Anonymous";
Gender = "Unspecified";
}
}
public class ToPage
{
public WebsiteTheme WebsiteTheme{ get; set; }
public User User { get; set; }
public ToPage() {
websiteTheme = new WebsiteTheme();
user = new User();
}
}
This will allow you to send any amount of classes to your page.
Then, in your controller, you'd want to populate those classes. Make sure to initialise them all first, then set the populated classes to your holder class.
WebsiteTheme websiteTheme = new WebsiteTheme();
websiteTheme.Color = "orange";
User user = new User();
user.Name = "Darren";
ToPage toPage = new ToPage();
toPage.User = user;
toPage.WebsiteTheme = websiteTheme;
return View(toPage);
In your view, you'd call them in any way you want to. But make sure to use HolderModel.SpecifiedModel in every case.
#model WebApplication1.Models.ToPage
#Html.DisplayFor(model => model.User.Name)
I did a compound model like this:
public class CompoundModel
{
public SearchModel SearchModel { get; set; }
public QueryResultRow ResultModel { get; set; }
}
public class QueryResultRow
{
[DisplayName("Id")]
public long id { get; set; }
[DisplayName("Importdatum")]
public System.DateTime importdate { get; set; }
[DisplayName("Mandant")]
public int indexBMClient { get; set; }
}
public class SearchModel
{
[Required]
[DataType(DataType.Date)]
[Display(Name = "Zeitraum von")]
public DateTime dateFrom { get; set; }
[Display(Name = "Terminal-ID")]
public string tid { get; set; }
[Display(Name = "Belegnummer")]
public string receiptnumber { get; set; }
}
In the view header:
#model MyProject_aspmvc.Models.CompoundModel
And get data access from the SearchModel, for example:
model => model.SearchModel.tid
and data access from the ResultModel, for example:
model => model.ResultModel.importdate
I need big help from here i am new to Asp.net MVC 4 Application development , actually i faced a problem when i save my dropdownlist selected value in a database ,after i click my submit button.
I use Debug pointer to check values in a HTTP post object but it doesn't contain dropdownlist select value it always display null value in a division raw I need some expert advice to solve that problem i go through the several examples and try several times but still i haven't proper solution for that.
Model class:
public partial class tblEmployee
{
public int EmployeeId { get; set; }
public string Title { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Nullable<System.DateTime> DateOfBirth { get; set; }
public Nullable<System.DateTime> DateOfJoin { get; set; }
public string Position { get; set; }
public string Office { get; set; }
public string Division { get; set; }
public Nullable<decimal> Salary { get; set; }
public virtual tblDivision Divisions { get; set; }
}
public partial class tblDivision
{
public int value { get; set; }
public string Division { get; set; }
public Nullable<int> SelectId { get; set; }
}
Controller class:
namespace EmpiteHrSystem.Controllers
{
public class EmployeeController : Controller
{
private EmpiteContext db = new EmpiteContext();
public ActionResult Create()
{
ViewBag.DivisionOptions = new SelectList(db.tblDivisions, "value","Division");
return View();
}
//
// POST: /Employee/Create
[HttpPost]
public ActionResult Create(tblEmployee tblemployee)
{
if (ModelState.IsValid)
{
try
{
db.Entry(tblemployee).State = EntityState.Added;
db.tblEmployees.Add(tblemployee);
db.SaveChanges();
}
catch (ArgumentException ae)
{
ModelState.AddModelError("", ae.Message);
}
return RedirectToAction("Index");
}
}
}
}
View:
#model EmpiteHrSystem.Models.tblEmployee
#{ ViewBag.Title = "Create"; Layout = "~/Views/Shared/_Layout.cshtml";}
#Html.EditorFor(model => model.EmployeeId)
#Html.ValidationMessageFor(model => model.EmployeeId)
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
#*#Html.EditorFor(model => model.Office)*#
#Html.DropDownList("DivisionOptions")
#Html.ValidationMessageFor(model => model.Division)
#Html.ActionLink("Back to List", "Index")
Your DivisionOptions drop down list is not being bound back to the model properly because it is the wrong name. Your model is looking for a property with the name Division while your drop down list is being bound to DivisionOptions. You have a few options.
Use a strongly typed helper
#Html.DropDownListFor(x=>x.Division, (SelectList)ViewBag.DivisionOptions)
Rename your currrent code and pass in the SelectList
#Html.DropDownList("Division", (SelectList)ViewBag.DivisionOptions)
I am a beginner ASP.net, I want to display all comment in post. Detail() method in PostController only shows post content, I don't know how to handle Detail() method to show commnent in post.
Hope someone can help me, Thank so much.
I have two model
public class Post
{
public int PostID { get; set; }
public string Title{ get; set; }
public string Content{ get; set; }
}
public class Comment
{
public int CommentID { get; set; }
public int PostID { get; set; }
public string Content { get; set; }
}
and this is Post Controller
public class PostController : Controller
{
//////
public ViewResult Detail(int id )
{
Post viewpost= (repository.Posts.Single(p => p.PostID == id));
return View(viewpost);
}
////////////////
}
Moralus has a good answer. If you don't want to change your Post model to have a navigation property to its comments, if out of your control or something, then you would have to make something like a ViewPostModel
public class ViewPostModel
{
public Post Post { get; set; }
public List<Comment> Comments { get; set; }
}
Then, you would have to query your repository for both lists:
public class PostController : Controller
{
public ViewResult Detail(int id )
{
ViewPostModel viewpost = new ViewPostModel();
viewPost.Post = repository.Posts.Single(p => p.PostID == id);
viewPost.Comments = repository.Comments.Where(c=> c.PostID = id).ToList();
return View(viewpost);
}
}
So first of all you should have a navigation property from post to it's comment.
something like this:
public class Post
{
public int PostID { get; set; }
public string Title{ get; set; }
public string Content{ get; set; }
public virtual IEnummrable<Comment> Comments {get; set;}
}
Note that if your using DB-First to do that you should edit EDMX
Then just take the comments from the repository like that:
public ViewResult Detail(int id )
{
Post viewpost= (repository.Posts.Include("Comments").Single(p => p.PostID == id));
return View(viewpost);
}
And in your view iterate trought your comments and display them like that
#foreach (var comment in Model.Comments)
{
#comment.Content
}
EDIT:
All of the above is relevant only if you use EF\ Linq-2-SQL (thanks #tostringtheory)
Also you can use the ViewData put the comments to the View.
eg:
Action:
ViewData["Comments"] = repository.Comments.Where(c=> c.PostID = id).ToList();
View:
var comments =ViewData["Comments"] as List;
...and you can use the var-comments