In the below example I'm trying to save the Id from the Asp.Net Identity, "aspnetusers" table "Id" column into my "Application" table. The dropdownlist of "fullname" populates, but is not saving to my "application" table when I submit an application. I need the "Id" from the aspnetusers table to be saved to my "application" table after submitting the application. Help would be greatly appreciated!
Controller:
private ApplicationDbContext db = new ApplicationDbContext();
public ActionResult Index()
{
ViewBag.FullNameList = new SelectList(db.Users,"Id","FullName");
return View();
}
// POST: Applications/Create
[ValidateAntiForgeryToken]
public ActionResult Index([Bind(Include = "Id,FullName, FirstName,MiddleInitial,LastName,")] Application application)
{
if (ModelState.IsValid)
{
ViewBag.FullNameList = new SelectList(db.Users, "Id", "FullName", application.ApplicationUser);
db.Applications.Add(application);
db.SaveChanges();
return RedirectToAction("Thanks");
}
}
View:
<p>
#Html.DropDownListFor(m => m.FullName, (SelectList)ViewBag.FullNameList, "Select Loan Officer")
</p>
Model:
public class Application
{
public int ApplicationId { get; set; }
[Required]
[DisplayName("First Name")]
public string FirstName { get; set; }
[DisplayName("Middle Initial")]
public string MiddleInitial { get; set; }
[Required]
[DisplayName("Last Name")]
public string LastName { get; set; }
public virtual string FullName {get; set; }
public virtual ApplicationUser ApplicationUser { get; set; }
}
Many things wrong:
Your post action accepts Application, which doesn't have a FullName property.
Even if it did, your Bind attribute doesn't included it.
You can't have a ViewBag member holding your select list with the same name as the field you're trying to post. Change it to something like ViewBag.FullNameChoices.
The posted value would be the Id of the "loan officer" user and you're doing nothing with it. If you actually had a foreign key property, you could post directly to that, but instead you're just relying on EF to create an implicit property which you have no access to. In your post action, you would need to look up the user with that Id from the database and then set your ApplicationUser property to that.
While not technically wrong, having a property that represents the "loan officer" for an application call ApplicationUser is not intuitive. You should change it to something like LoanOfficer. Also, it looks like your assuming that all users for all time will also be "loan officers", you should probably plan some flexibility by creating a subclass of ApplicationUser for a loan officer or use roles to assign that status.
Are you forgetting to add an [HttpPost]?
Also, your DropDownList might be:
#Html.DropDownList("FullName", ViewBag.FullName, "Select Loan Officer")
Related
There is My Admin Model:
[Required]
public int AdminId { get; set; }
[Required]
public string Password { get; set; }
[Required]
public string Name {get; set;}
I need user Login (not register, so I don't need Name property):
So , there is 2 property:
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.EditorFor(model => model.AdminId)
#Html.EditorFor(model => model.Password)
This is the Action:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index([Bind(Include = "AdminId, Password", Exclude = "Name")]Admin admin)
{
if (ModelState.IsValid)
{
//
}
}
But The ModelState.IsValid Always return false, tell me Name property not exists.
I know I use [Required], but it's only for register, not for login, I even not add Name property when Login. Why still return false? How Can I fixed it?
Update 1
I just add a another new property:
[Required]
public int WorkPlace { get; set; }
But It not be valid by ModelState.IsValid......but Why Name Need?? because of string type?
Update 2
Looks like It's a some kind of BUG. because the int type can remove
use Exclude. but the string type can't ......
Its not a bug. Using the Bind.Exclude just means even if the property value is posted back, don't set its value in my model. Its used to prevent over posting attacks as explained in more detail here. int type works in your case because the default value for int is 0 so its valid, but for string the default is null so [Required] fails. The best way to handle this is to create a view model that contains only the properties you want to edit
public class LogInVM
{
[Required]
public int AdminId { get; set; }
[Required]
public string Password { get; set; }
}
and then post back that model (no Bind attribute is required)
public ActionResult Index(LogInVM model)
{
....
Your admin object is just a parameter. But your Admin View Model requires a Name property. Your model state is thus correctly invalid when the Name property is missing.
Two quick solutions would be :
Remove the Required attribute
Create a new ViewModel for Login
I'd opt for 2
Update
Actually I didn't know this existed, but you can use ModelState.Remove as per this answer
I have two models as below
public class Category
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ID { get; set; },
[Required]
public string category { get; set; }
[Required]
public string Desc { get; set; }
}
public class Product
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ID { get; set; },
public int CatID { get; set; },
[ForeignKey("CatID")]
public virtual Category Category { get; set; },
[Required]
public string Desc { get; set; },
public string DisplayName
{
get
{
return string.format("{0} - {1}",this.Category.category,this.Desc);
}
}
}
This is my Edit Action
public ActionResult Edit(int id)
{
ViewBag.PossibleCategories = categoryRepository.All;
return View(productRepository.Find(id));
}
[HttpPost]
public ActionResult Edit(Product product)
{
if (ModelState.IsValid) //<== This becomes false saying category.desc is required
{
productRepository.InsertOrUpdate(product);
productRepository.Save();
return RedirectToAction("Index");
}
else
{
ViewBag.PossibleCategories = categoryRepository.All;
return View();
}
}
I have a scaffolded a Edit view of product and it shows ID and DisplayName as Readonly. All the other fields a editable.
The edit view also has the product -> category -> category has a read-only text field
#Html.TextBoxFor(model => model.Category.category, new Dictionary<string, object>() { { "readonly", "true" } })
The Post back sends this and tries to create a new category. This is not required. The category link will be carried forward using the product.CatID.
How can i display these types of fields??
When the Edit view Post back the Model state appears as invalid because the product's category's desc is null (product -> category -> desc).
if i comment out the DisplayName property in Product this issue doesn't occur.
From my understanding, this is because the DiaplayName property refers to Category property and the view view doesn't have category.desc field so when the model is created back on the POST action, the desc is not populated. Adding the category.desc field to the view is one way of solving this problem.
Is there any other method to solve this?
Note: This is not the only model i'm having this issue. There are many complex models which have the same problem and to me having these fields also included in the view would make for (1) a very cluttered view (2) the amount of data making the round trip will be high.
Simple Solution
Check for null. Really you should be making this a habit anyway.
public string DisplayName
{
get
{
if(this.Category != null)
{
return string.format("{0} - {1}",this.Category.category,this.Desc);
}
else
{
return String.Empty;
}
}
}
Complex Solution
Instead of directly using your database model in your Views another solution is to create ViewModels. These are models meant specifically for your View. As a simplified example, let's take your Product model and create a ViewModel.
Create a folder for your ViewModels
Create ViewModel files that match your Controller
Create a ViewModel that you will use in your View
Say you have a Store Controller. This would be the file structure you would create.
Models
ViewModels
StoreViewModels.cs
Inside the StoreViewModels you would create a ViewModel called ProductViewModel which you would fill in with information from Product.
public class ProductViewModel
{
public int ID { get; set; }
public string Description { get; set; }
public string DisplayName { get; set; }
public ProductViewModel() { }
public ProductViewModel(Product product)
{
this.ID = product.ID;
this.Description = product.Description;
this.DisplayName = product.DisplayName;
}
}
In your View you reference ProductViewModel instead of Product. On the receiving end you then translate the ViewModel fields back to your Model. Let me know if you have any questions.
Its so simply, sadly this new Identity system has to throw a wrench at me.
All I want to do is in my admin, create a client project that is assigned to a user. There's plenty of documentation on how to get the user to create their own stuff. But I need to have the admin create only this time.
The page loads but then on post I get this error which makes no sense in this situation based off of what I've read, "There is no ViewData item of type 'IEnumerable' that has the key 'userId'." I'm clearly not using a ViewData and it clearly says "userId" on the dropdown.
The model should validate!
MODEL
public class ApplicationUser : IdentityUser
{
public virtual ICollection<ClientProject> ClientProjects { get; set; }
}
public class ClientProject
{
public int Id { get; set; }
[Display(Name = "Project")]
[Required(ErrorMessage = "A project name is required.")]
public string Name { get; set; }
// ForeignKey => dbo.IdentityUser
[Display(Name = "Client")]
[Required(ErrorMessage = "Please select a client account to associate with.")]
public virtual ApplicationUser User { get; set; }
}
CONTROLLER
// GET: /Admin/ClientProjects/Create
public ActionResult Create()
{
ViewBag.ProjectStatusId = new SelectList(Db.ProjectStatuses, "Id", "Name");
ViewBag.Users = new SelectList(UserManager.Users.ToList(),"Id", "UserName");
return View();
}
// POST: /Admin/ClientProjects/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create([Bind(Include="Id,Name,ProjectStatusId")] ClientProject clientproject, string userId)
{
var client = UserManager.FindById(userId);
if (ModelState.IsValid)
{
clientproject.User = client;
Db.ClientProjects.Add(clientproject);
await Db.SaveChangesAsync();
return RedirectToAction("Index");
}
ViewBag.ProjectStatusId = new SelectList(Db.ProjectStatuses, "Id", "Name", clientproject.ProjectStatusId);
return View(clientproject);
}
VIEW
#Html.DropDownList("userId", (IEnumerable<SelectListItem>)ViewBag.Users, "--Select Client Account--", new { #class = "form-control" })
ViewData is MVC's internal representation of your view model, a key-value dictionary to which it refers when trying to bind the data you pass to the model you assign to the view. So in your case, it has no idea what userId is more than likely because your Users view model has an Id property and that's what it expects.
Basically, this MVC error means "I was looking for property [blank] in your model but couldn't find it, therefore I can't complete the task."
I have an application that allows user to create new items. As it stands now the user need to fill out Title, body and category.
The category is a textbox but I would like to convert it to a dropdownlist. This is all connected to a database and when the user submits it the data should be saved in the database. Everything works fine as it stands now, I am only having trouble implementing the dropdownlist.
My model:
public class NewsItem
{
public int ID { get; set; }
[Required(ErrorMessage = "Title is Required")]
public string Title { get; set; }
[Required(ErrorMessage = "Body is Required")]
public DateTime DateCreate { get; set; }
public string Category { get; set; }
}
What is the quickest/best way implementing this. Should I do it in the model or can I assign the values available in the view?
Thanks in advance!
First, some semantics. Since Entity Framework comes along with MVC, the assumption is that your POCOs are "models". (Unfortunately, Microsoft kind of piles on by putting scaffolded POCOs in a "Models" folder). However, these are not models in terms of the "Model" in Model-View-Controller; instead, they are merely "entities", which is a fancy way of saying "pretty much just a DTO EF can use to stuff data from the database into."
I point that out to say this: no, you shouldn't put your drop down list choices on your entity. But, you shouldn't rely on the view for this either. What you really want here is a view model. You create a class that has just the fields you need to edit and any additional business logic your view needs (such as the choices of categories), and then you map your entity to and from this view model. As an example:
public class NewsItemViewModel
{
[Required(ErrorMessage = "Title is Required")]
public string Title { get; set; }
[Required(ErrorMessage = "Body is Required")]
public DateTime DateCreate { get; set; }
public string Category { get; set; }
public IEnumerable<SelectListItem> CategoryChoices { get; set; }
}
Notice that while this class is mostly the same, it doesn't contain an Id property, because this is not something you'd want the user to edit. Also, it includes a CategoryChoices property to hold the items for your drop down list.
Then in your controller, you would do something like:
public ActionResult CreateNewsItem()
{
var model = new NewsItemViewModel();
model.CategoryChoices = db.Categories.Select(m => new SelectListItem { Value = m.Name, Text = m.Name });
return View(model);
}
Basically, you're just newing up the view model so you can feed it to the view. You fill in your category choices before actually returning it, though. I've assumed they're also entities, but you would use whatever method you needed to fetch them here, otherwise.
For your post action:
[HttpPost]
public ActionResult CreateNewsItem(NewsItemViewModel model)
{
if (ModelState.IsValid)
{
// map view model to entity
var newsItem = new NewsItem
{
Title = model.Title,
Category = model.Category,
// and so on
}
db.NewsItems.Add(newsItem);
db.SaveChanges();
return RedirectToAction("Index");
}
model.CategoryChoices = db.Categories.Select(m => new SelectListItem { Value = m.Name, Text = m.Name });
return View(model);
}
I'm just doing a manual mapping from the view model to a new news item, here, but for real world scenarios, you'd probably want to integrate a mapping library for this, such as AutoMapper. Also, take note that in the case where there's an error, you must refill the category choices before returning the view again. These will not be posted by your form, so the model that was passed in will not have them.
Finally, in your view:
#model Namespace.To.NewsItemViewModel
...
#Html.DropDownListFor(m => m.Category, Model.CategoryChoices)
I am getting a simple object from a form in a MVC4 application
portfolio = {code: xxxxx, quantity: -10}
I need to add the Username to the database when I do this insert which I already know I can get from the HTTPContext.
What would be the best way to include this in the below code. I know I will need to change the object I am adding but what is the right way to do this?
public ActionResult Add(Portfolio portfolio)
{
var ctx = new MarginEntities();
ctx.Portfolios.Add(portfolio);
ctx.SaveChanges();
return RedirectToAction("Index");
}
Extend the Portfolio class to include the necessary property:
class Portfolio
{
public string Code { get; set; }
public int Quantity { get; set; }
public string Username { get; set; }
}
When the request comes from client, fields Code and Quantity are initialized, and Username is set to its default value, which is null. So simply initialize it before submitting to DB:
public ActionResult Add(Portfolio portfolio)
{
portfolio.Username = Session["Username"]; // or however the username is stored
var ctx = new MarginEntities();
ctx.Portfolios.Add(portfolio);
ctx.SaveChanges();
return RedirectToAction("Index");
}
You could use the User object, I take it all users are first authenticated?
User.Identity.Name
Your Portfolio object could then either have a string for the username or you could use a FK to the 'users' table, and get this ID by passing in the Name property from above.
You could then have either:
class Portfolio
{
...
public string Username { get; set; }
}
If you take this approach, then you would just pass the Portfolio object with the new Username property set.
or
class Portfolio
{
...
public int UserId { get; set; }
}
if you take this approach then you would need to request the UserId from EF context and then populate the Portfolio object with the returned UserId