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."
Related
I want to make a dropdownlist which shows every category id and name. Unfortunately when i start, it shows the following error:
"There is no ViewData item of type 'IEnumerable' that has the key '...'."
In the view:
#model WebApplication1.Models.Zoekitem
#{
ViewBag.Title = "Create";
}
#Html.DropDownList("list1", ViewBag.categorieBag as SelectList, "-- Select --")
In the controller:
FAQDBConnection FAQconnection = new FAQDBConnection();
var getlist = FAQconnection.Categorie.ToList();
SelectList list = new SelectList(getlist, "ID", "Naam");
ViewBag.categorieBag = list;
In the model:
public class Categorie
{
[Key]
public int ID { get; set; }
public string Naam { get; set; }
}
public class FAQDBConnection : DbContext
{
public FAQDBConnection()
: base("FAQDBConnection")
{
//disable initializer
Database.SetInitializer<FAQDBConnection>(null);
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
public DbSet<Categorie> Categorie { get; set; }
public DbSet<Zoekitem> Zoekitem { get; set; }
public DbSet<ZoekitemCategorie> ZoekitemCategorie { get; set; }
}
Every single time i start the program, it says that there is no viewdata of the first string given in the dropdownlist. In this case: "list1".
Just so you know, the #model WebApplication1.Models.Zoekitem is there for the some other textboxes included for the create page.
Thanks in advance!
Try to replace as SelectedList with IEnumerable<SelectListItem>
#Html.DropDownList("list1", ViewBag.categorieBag as IEnumerable<SelectListItem>, "-- Select --")
Okay, so after speaking to a colleague about it, we managed to solve the problem.
In my SQL database I had a third table, this one was meant to pair the id's from two other tables.
In our controller we had the code in the POST action method, this had to be in the GET action method.
Also in our model we didn't define our keys. Keep note that defining our keys as in [Key] didn't do the trick on its own. [Column(Order = 0)] and [Column(Order = 1)] had to be added to define which one is more important.
Controller:
// GET: Zoekitem/Create
public ActionResult Create()
{
FAQDBConnection FAQconnection = new FAQDBConnection();
var getlist = FAQconnection.Categorie.ToList();
SelectList list = new SelectList(getlist, "ID", "Naam");
ViewBag.categorieBag = list;
return View();
}
// POST: Zoekitem/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Zoekitem zoekitem, string catID, string catNaam)
{
if (ModelState.IsValid)
{
db.Zoekitem.Add(zoekitem);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(db.Categorie.ToList());
}
Model:
public class ZoekitemCategorie
{
[Key]
[Column(Order = 0)]
public int IDZ { get; set; }
[Key]
[Column(Order = 1)]
public int IDC { get; set; }
}
View:
#Html.DropDownList("list1", ViewBag.categorieBag as IEnumerable<SelectListItem>, "-- Select --")
(Feel free to correct me on mistakes!)
This question already has answers here:
The ViewData item that has the key 'XXX' is of type 'System.Int32' but must be of type 'IEnumerable<SelectListItem>'
(6 answers)
Closed 6 years ago.
I have went through all other questions about this problem. Tried posted solution tens of times, none works.
I have two classes:
public class Player
{
public int Id { get; set; }
[MinLength(1)]
public string Name { get; set; }
[MinLength(4)]
public string LastName { get; set; }
[DataType(DataType.Date)]
public DateTime Birthdate { get; set; }
public virtual Club Club { get; set; }
}
And
public class Club
{
public int Id { get; set; }
[StringLength(30, MinimumLength = 3)]
[Required]
public string Name { get; set; }
[DataType(DataType.Date)]
public DateTime Founded { get; set; }
public virtual ICollection<Player> Players { get; set; }
public override string ToString() {
return Name;
}
}
In Create action method of Player i have:
// GET: /Players/Create
public ActionResult Create() {
//SelectList selectList = new SelectList(db.Clubs, "Id", "Name");
ViewBag.Clubs = new SelectList(db.Clubs, "Id", "Name");
return View();
}
// POST: /Players/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Id,Name,LastName,Birthdate,Club")] Player player) {
if (ModelState.IsValid) {
db.Players.Add(player);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(player);
}
In the form I try I have dropdown list for to select the club in which player plays:
<div class="form-group">
#Html.LabelFor(model => model.Club, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(player => player.Club, (SelectList)ViewBag.Clubs)
#Html.ValidationMessageFor(model => model.Club)
</div>
</div>
the dropdown list is rendered correctly, but when I accept the form I get yellow screen of death saying: There is no ViewData item of type 'IEnumerable<SelectListItem>' that has the key 'Club'.
Dropdown list looks like this.
ViewData only exists for the current response. When you return from the POST overload of Create there is nothing to populate ViewData.
The normal pattern in these cases is to, from the POST action, to redirect to the GET action. This avoids repeating code and ensures that the user refreshing the page does not post the previous values again.
You should regenerate the SelectList upon post. ViewBag's lifetime is per request only that's why it's already destroyed when you reached POST failure.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Id,Name,LastName,Birthdate,Club")] Player player) {
if (ModelState.IsValid) {
db.Players.Add(player);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.Clubs = new SelectList(db.Clubs, "Id", "Name");
return View(player);
}
Or you can just redirect it to your GET version so your GET action will be called which means you don't need to regenerate the list upon post.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Id,Name,LastName,Birthdate,Club")] Player player) {
if (ModelState.IsValid) {
db.Players.Add(player);
db.SaveChanges();
return RedirectToAction("Index");
}
return RedirectToAction("Create", player);
}
I am trying to pass a value which is stored in one controller to another, code is below:
Charities Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Donate([Bind(Include = "ID,DisplayName,Date,Amount,Comment")] Charity charity)
{
if (ModelState.IsValid)
{
if (!string.IsNullOrEmpty(charity.Comment))
{
var comment = charity.Comment.ToLower().Replace("hot", "###").Replace("cold", "###").Replace("Slow", "###").Replace("enjoy", "###").Replace("BAD", "###");
charity.Comment = comment; //Replaces textx from model variable - comment
charity.TaxBonus = 0.20 * charity.Amount;
}
if (string.IsNullOrEmpty(charity.DisplayName))
{
charity.DisplayName = "Annonymus"; //If user doesnt enter name then Annonymus
}
db.Donations.Add(charity);
db.SaveChanges();
TempData["Name"] = charity.DisplayName;
TempData["Amount"] = charity.Amount;
TempData["Comment"] = charity.Comment;
return RedirectToAction("../Payments/Payment", "Charities", new { id = charity.Amount });
}
return View(charity);
}
Charities Class
public class Charity
{
public int ID { get; set; }
[RegularExpression(#"^[a-zA-Z]+$", ErrorMessage = "Use letters only please")]
public string DisplayName { get; set; }
[DataType(DataType.Currency)]
[Range(2, Int32.MaxValue, ErrorMessage = "Atleast £2.00 or a whole number please")]
public int Amount { get; set; }
[DataType(DataType.Currency)]
public Double TaxBonus { get; set; }
public String Comment { get; set; }
public static object Information { get; internal set; }
}
Payment Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Payment([Bind(Include = "ID,CardName,CardNumber,ValidFrom,Expires,CardSecurityCode,EmailAddress,ConfrimEmailAddress,Address,City,Country,PostCode")] Payment payment)
{
if (ModelState.IsValid)
{
db.Payments.Add(payment);
db.SaveChanges();
TempData["Name"] = charity.DisplayName
TempData["Amount"];
TempData["Comment"];
TempData["Name"] = payment.CardName;
TempData["Email"] = payment.EmailAddress;
return RedirectToAction("Confirmation", "Payments", new { id = payment.ID });
}
return View(payment);
}
Payment Class
public class Payment
{
public int ID { get; set; }
}
public class CharityDBContext : DbContext //controls information in database
{
public DbSet<Charity> Donations { get; set; } //creates a donation database
}
public class PaymentDBContext : DbContext //controls information in database
{
public DbSet<Payment> Payments { get; set; } //creates a donation database
public System.Data.Entity.DbSet<CharitySite.Models.Charity> Charities { get; set; }
}
}
I am trying to get this from the Charities Controller
TempData["Name"] = charity.DisplayName;
To display in Payment controller
TempData["Name"] = charity.DisplayName;
Right now theres a squigly red line under "charity" in the payment controller with the message - doesnt exist in current context. I just wanted to know if it is possible to pass data from different controllers using temp data.
First of all, the line return RedirectToAction in your Donate method is going to send a 302 response to your browser which will issue a GET request to the url in the location header of the response, which in this case is Payment/Payment. But your Payment method is marked with HttpPost. Are you sure you want to send a second GET request to a method marked with HttpPost to save some part of the data(Payment) you want to save ?
I think you should save your charity and payment info in the same action method( Create a PaymentCharity view model and use that instead of using the Bind attribute and the entity classes created by EF to transfer data from your view to action method). Also, insteaof using TempData to pass data, What you should do is, get the unique id of the Payment record you saved, pass that in querystring to the second action method and in that using the unique payment id,read the payment record again and use that.
So in your Donate method,
public ActionResult Donate(PaymentCharirtVm model)
{
var charity = new Charity { DisplayName =model.Amount,Comment =model.Comment};
var payment = new Payment ();
//set the properties of payment here
db.Donations.Add(charity);
db.SaveChanges();
//now save Payment
db.Payment.Add(payment);
db.SaveChanges();
return RedirectToAction("Confirmation","Payment", new { id=payment.Id });
}
I have never tried to do it with tempdata. Have never needed to. I recomend you add the string with the charity name to the view model used by the payment controller.
Please correct code as below to retrieve value from TempData.
charity.DisplayName= TempData["Name"]
As already answered here, most answer are correct. It would be better to use routeValues in the RedirectToAction method to pass value from one action to another action in the same controller or different controller.
return RedirectToAction("actionName", "anotherControllerName", new { id = charity.DisplayName });
In the another controller, data can be retrieved by
string displayName = RouteData.Values["id"].ToString();
Here, in the routeValues, you can pass the whole Charity object as well and do some casting in the another controller to get the object properly in the that case when you need more than one properties to be sent:
return RedirectToAction("actionName", "anotherControllerName", new { id = charity });
And get it by:
Charity chatiry = (Charity)RouteData.Values["id"];
I hope that you will get some idea from this.
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")
I'm having trouble passing a viewmodel into a view. I have two views: a Search view and a GeneralForm view. Search passes search results into GeneralForm's viewmodel.
Say the GeneralForm is a complex viewmodel that holds two other viewmodels:
public class GeneralFormViewModel
{
public GeneralInfoViewModel GeneralInfo { get; set; }
public NotesViewModel Notes { get; set; }
}
public class GeneralInfoViewModel
{
[Required(ErrorMessage = "Please enter the person's name.")]
[DisplayName("Name:")]
public string Name { get; set; }
[Required(ErrorMessage = "Please enter the person's ID.")]
[DisplayName("ID:")]
public int ID { get; set; }
}
public class NotesViewModel
{ // etc.
(I set up this way in order to use multiple #Html.BeginForms on my GeneralForm view. In this way, I hope to POST and validate small sections of the entire general form, one at a time, using KnockoutJS and AJAX.)
[HttpPost]
public ActionResult Search(SearchViewModel vm)
{
var query = // do some searching
var viewmodel = new GeneralFormViewModel()
{
GeneralInfo = new GeneralInformationViewModel
{
ID = query.id,
Name = query.name
}
};
return RedirectToAction("GeneralForm", viewmodel);
}
At this point, viewmodel.GeneralInfo is not null, and the viewmodel is passed to the GeneralForm controller.
[HttpGet]
public ActionResult GeneralForm(GeneralFormViewModel model)
{
return View(model);
}
Now model.GeneralInfo is null. What conventions of MVC am I breaking by doing this, and how can I get the GeneralForm view to render the data acquired via the Search controller to the GeneralForm view?
Problem is You can't send data with a RedirectAction.
you're doing a 301 redirection and that goes back to the client.
Store it in a TempData or Session ...
See the following post:
passing model and parameter with RedirectToAction