Asp.Net MVC dropdownlist not posting value to controller -> database - asp.net-mvc

my development environment is ASP.NET MVC 5 using Entity Framework 6 using a code-first workflow.
My problem: When trying to add a new record to the database, the values from my two dropdownlists are not being included. All the other fields are indeed saving to the database.
I am passing a ViewModel to the view in question:
public class NewItemViewModel
{
public IEnumerable<Category> Categories { get; set; }
public Item Item { get; set; }
public IEnumerable<Donor> Donors { get; set; }
}
The domain models I am using:
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int Quantity { get; set; }
public bool IsActive { get; set; }
public Category Category { get; set; }
public Donor Donor { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Donor
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName { get { return FirstName + " " + LastName; } }
public string Email { get; set; }
}
The relevant controller: This New() action is simply used to pass data to the view for the form to be submitted
public ActionResult New()
{
var itemCategories = _context.ItemCategory.ToList();
var donors = _context.Donors.ToList();
var viewModel = new NewItemViewModel
{
Categories = itemCategories,
Donors = donors
};
return View(viewModel);
}
Here is my actual action that adds the submitted data to the database:
// Create a new item.
[HttpPost]
public ActionResult Create(Item item)
{
_context.Items.Add(item);
_context.SaveChanges();
return RedirectToAction("Index", "Item");
}
And lastly, the view itself (I will minimize the code here):
#using (Html.BeginForm("Create", "Item"))
{
<div class="form-group">
#Html.LabelFor(m => m.Item.Donor)
#Html.DropDownListFor(m => m.Item.Donor, new SelectList(Model.Donors, "Id", "FullName"), "Who donated this item?", new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.Item.Category)
#Html.DropDownListFor(m => m.Item.Category, new SelectList(Model.Categories, "Id", "Name"), "Select Item Category", new { #class = "form-control" })
</div>
}
To reiterate the problem I am having: The values of the dropdownlist for Category and Donor are not saving to the database, whereas the others (non-navigation properties?) Name, Description, Quantity, etc. is working correctly.
Am I using the ViewModel the right way? I was under the impression that the MVC framework knows how to deal with passing the Item object parameter in the Create() action - mapping what it needs to within the Item entity.
Any help would be much appreciated. Thank you.

In the NewItemViewModel you have not created property to hold the selected values from dropdown
public class NewItemViewModel
{
public IEnumerable<Category> Categories { get; set; }
public int SelectedCategory{get;set;}
public Item Item { get; set; }
public int SelectedDonor{get;set;}
public IEnumerable<Donor> Donors { get; set; }
}
#using (Html.BeginForm("Create", "Item"))
{
<div class="form-group">
#Html.LabelFor(m => m.Item.Donor)
#Html.DropDownListFor(m => m.SelectedDonor, new SelectList(Model.Donors, "Id", "FullName"), "Who donated this item?", new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.Item.Category)
#Html.DropDownListFor(m => m.SelectedCategory, new SelectList(Model.Categories, "Id", "Name"), "Select Item Category", new { #class = "form-control" })
</div>
}

Related

MVC Razor two level Model for ListBoxFor

Is there a way in the view to drill down one level to get the data object in the GenericValues? I know that I can change the model around to make it work but I didnt want to add extra model classes?
View
<div class="form-group row">
#Html.LabelFor(model => model.selectedLanguage)
#Html.ListBoxFor(
x => x.selectedLanguage,
new SelectList(Model.ListOfLanguages, "Value.Value", "Value.Name"),
new { id = "multiselectLanguage", #class = "form-control" })
</div>
Model
public List<LanguageModel> ListOfLanguages { get; set; }
public class LanguageModel
{
public string ISOCode { get; set; }
public GenericValues Values { get; set; }
}
public class GenericValues
{
public string Name { get; set; }
public int Value { get; set; }
}
You can use linq to get list<GenericValues>:
#Html.ListBoxFor(
x => x.selectedLanguage,
new SelectList(Model.ListOfLanguages.Select(x => x.Values).Distinct(), "Value", "Name"),
new { id = "multiselectLanguage", #class = "form-control" })

How do I pass selected items in MVC4 EF5?

I am new to MVC and would appreciate any advice. I have several models/tables that work together. The trouble I am having is with a many-to-many relationship. What I want is to have a listbox that a user can multiselect from and pass those values to save in join table, while saving the primary entry to another table.
My models:
public class Card
{
public virtual int CardID { get; set; }
public virtual string Title { get; set; }
//A bunch of properties...
//Drop Down Lists
public int RarityID { get; set; }
public virtual Rarity Rarity { get; set; }
public int MainTypeID { get; set; }
public virtual MainType MainType { get; set; }
public int CardSetID { get; set; }
public virtual CardSet CardSet { get; set; }
public int SubTypeID { get; set; }
public virtual SubType SubType { get; set; }
public virtual string AdditionalType { get; set; }
public virtual IList<CardAbility> Abilities { get; set; }
public virtual int[] SelectedAbilities { get; set; }
}
public class Ability
{
public virtual int AbilityID { get; set; }
public virtual string Title { get; set; }
public virtual IList<CardAbility> Cards { get; set; }
}
public class CardAbility
{
public int CardAbilityID { get; set; }
public virtual Ability Ability { get; set; }
public int AbilityID { get; set; }
public virtual Card Card { get; set; }
public int CardID { get; set; }
}
My Controller:
public ActionResult Create()
{
ViewBag.RarityID = new SelectList(db.Rarities, "RarityID", "Title");
ViewBag.MainTypeID = new SelectList(db.MainTypes, "MainTypeID", "Title");
ViewBag.CardSetID = new SelectList(db.CardSets, "CardSetID", "Title");
ViewBag.SubTypeID = new SelectList(db.SubTypes, "SubTypeID", "Title");
ViewBag.Abilities = new MultiSelectList(db.Abilities, "AbilityID", "Title");
return View();
}
// POST: /Card/Create
[HttpPost]
public ActionResult Create(Card card)
//[ModelBinder(typeof(CardBinder1))]
{
if (ModelState.IsValid)
{
db.Cards.Add(card);
db.SaveChanges();
foreach (var items in card.SelectedAbilities)
{
var obj = new CardAbility() { AbilityID = items, CardID = card.CardID };
db.CardAbilities.Add(obj);
}
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.RarityID = new SelectList(db.Rarities, "RarityID", "Title", card.RarityID);
ViewBag.MainTypeID = new SelectList(db.MainTypes, "MainTypeID", "Title", card.MainTypeID);
ViewBag.CardSetID = new SelectList(db.CardSets, "CardSetID", "Title", card.CardSetID);
ViewBag.SubTypeID = new SelectList(db.SubTypes, "SubTypeID", "Title", card.SubTypeID);
ViewBag.Abilities = new MultiSelectList(db.Abilities, "AbilityID", "Title");
return View(card);
My Create View:
model MTG.Models.Card
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Card</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Title)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Title)
#Html.ValidationMessageFor(model => model.Title)
</div>
<div class="editor-label">
#Html.Label("Abilities")
</div>
<div class="editor-field">
#Html.ListBoxFor(model => model.Abilities, (ViewBag.AbilityID as MultiSelectList))
#Html.ValidationMessageFor(model => model.Abilities)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.RarityID, "Rarity")
</div>
<div class="editor-field">
#Html.DropDownList("RarityID", String.Empty)
#Html.ValidationMessageFor(model => model.RarityID)
</div>
// A lot more fields...
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
My DBContext:
public DbSet<Ability> Abilities { get; set; }
public DbSet<Rarity> Rarities { get; set; }
public DbSet<CardSet> CardSets { get; set; }
public DbSet<MainType> MainTypes { get; set; }
public DbSet<SubType> SubTypes { get; set; }
public DbSet<Card> Cards { get; set; }
public DbSet<CardAbility> CardAbilities { get; set; }
public class AbilitiesToCardsConfiguration : EntityTypeConfiguration<CardAbility>
{
internal AbilitiesToCardsConfiguration()
{
this.HasKey(p => new { p.AbilityID, p.CardID });
this.HasRequired(p => p.Ability)
.WithMany(p => p.Cards)
.HasForeignKey(p => p.AbilityID);
this.HasRequired(p => p.Card)
.WithMany(r => r.Abilities)
.HasForeignKey(p => p.CardID);
}
}
I have been working on this for about 3 days and have done a lot of trial and error from what I have read online. At this point, the create view does display a listbox that is pulling the titles from the Abilities table. When I try to save, I get a validation error "The value "1" is invalid.", where 1 is the ID for that ability. When debugging, I see that the modelstate is invalid and the error is
{System.InvalidOperationException: The parameter conversion from type 'System.String' to type 'MTG.Models.CardAbility' failed because no type converter can convert between these types.
at System.Web.Mvc.ValueProviderResult.ConvertSimpleType(CultureInfo culture, Object value, Type destinationType)
at System.Web.Mvc.ValueProviderResult.UnwrapPossibleArrayType(CultureInfo culture, Object value, Type destinationType)
at System.Web.Mvc.ValueProviderResult.ConvertTo(Type type, CultureInfo culture)
at System.Web.Mvc.DefaultModelBinder.ConvertProviderResult(ModelStateDictionary modelState, String modelStateKey, ValueProviderResult valueProviderResult, Type destinationType)}
I know it doesn't like the types and can't convert, but if I try anything else with the listboxfor helper it won't bring in the data and usually crashes before I even get to see the create page. Sorry this is so long, I just wanted to give all the information I could. :) Thank you for any help.
Generate a listbox for SelectedAbilities instead of Abilities:
#Html.ListBoxFor(model => model.SelectedAbilities , (ViewBag.AbilityID as MultiSelectList))
By the way you need to do the same for RarityID instead of Rarity, MainTypeID instead of MainType and etc.

DropDownList MVC returns null

I've encountered some problems using DropDownList in ASP.NET MVC lately.
I want to save value of selected item to member called Wydzialy.
Sorry for not translating some names, they doesn't matter I think :)
Here is what I have:
View:
<div class="form-group">
#Html.LabelFor(model => model.Wydzial, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(x => x.Wydzial, (List<SelectListItem>)ViewBag.Wydzialy)
</div>
</div>
Model:
public class Student
{
public int Id { get; set; }
public int NumerIndeksu { get; set; }
public string Imie { get; set; }
public string Nazwisko { get; set; }
public int Semestr { get; set; }
public virtual Wydzial Wydzial { get; set; }
}
Controller:
public ActionResult Create()
{
var wydzialy = db.Wydzialy.ToList();
var lista = wydzialy.Select(W => new SelectListItem()
{
Text = W.Nazwa
}).ToList();
ViewBag.Wydzialy = lista;
return View();
}
Your trying to bind the dropdown to a complex object. A <Select> only posts backs a value type (in your case the text of the selected option).
Either bind to a property of Wydzial
#Html.DropDownListFor(x => x.Wydzial.Nazwa, (List<SelectListItem>)ViewBag.Wydzialy)
or preferably use a view model that includes a property to bind to and the SelectList
public class StudentVM
{
public int Id { get; set; }
// Other properties used by the view
public string Wydzial { get; set; }
public SelectList Wydzialy { get; set; }
}
Controller
public ActionResult Create()
{
StudentVM model = new StudentVM();
model.Wydzialy = new SelectList(db.Wydzialy, "Nazwa", "Nazwa")
return View(model );
}
View
#model StudentVM
....
#Html.DropDownListFor(x => x.Wydzial, Model.Wydzialy)
Note you appear to be binding only to the Nazwa property of Wydzial. Typically ou would display a text property but bind to an ID property.

Create ActionResult for save throws error saying The model item passed into the dictionary is of type

Although this error is very common in the forum, but i am not able to understand how to fix it in my project. I am new to MVC framework.
View code:-
#model ClassifiedProject.Models.CreateAdvertVM
<div class="editor-label">#Html.LabelFor(model => model.AdvTitle) <i>(E.g. Old Samsung Galaxy Tab 2)</i></div>
<div class="editor-field">
#Html.EditorFor(model => model.AdvTitle)
#Html.ValidationMessageFor(model => model.AdvTitle)
</div>
<div class="editor-label">#Html.LabelFor(model => model.AdvDescription)</div>
<div class="editor-field">
#Html.TextAreaFor(model => model.AdvDescription)
#Html.ValidationMessageFor(model => model.AdvDescription)
</div>
<div class="editor-label">#Html.Label("Advertisement Category")</div>
<div class="editor-label">
#Html.DropDownListFor(model => model.SelectedCategoryId, Model.Categories, new { #class = "ddlcs" })
#Html.ValidationMessageFor(model => model.SelectedCategoryId)
</div>
<p><input type="submit" value="Save" /></p>
Controller code of Save button actionresult:-
[HttpPost]
public ActionResult Create(TR_ADVERTISEMENT tr_advert)
{
if (ModelState.IsValid)
{
tr_advert.CreatedDate = tr_advert.ModifiedDate = DateTime.Now;
if (tr_advert.IsPriceOnRequest)
{
tr_advert.CurrencyID = 0;
tr_advert.Price = 0;
}
db.ADVERTISEMENT.Add(tr_advert);
db.SaveChanges();
return RedirectToAction("Index");
}
Controller code for the form in render stage:-
// GET: /Advert/Create
public ActionResult Create()
{
var model = new CreateAdvertVM();
ViewBag.Message = "Post New Advertisement.";
////Render Category DDL
var cat = from s in db.CategoryDbSet
where s.IsActive == true
orderby s.CatName
select new { s.CatID, s.CatName };
var catListItems = cat.ToList().Select(c => new SelectListItem
{
Text = c.CatName,
Value = c.CatID.ToString()
}).ToList();
catListItems.Insert(0, new SelectListItem { Text = "[--Select the category--]", Value = "" });
model.Categories = catListItems;
return View(model);
ViewModel inherited from EF class:-
[NotMapped]
public class CreateAdvertVM : TR_ADVERTISEMENT
{
[DisplayName("Category")]
[Required]
public int? SelectedCategoryId { get; set; }
public IEnumerable<SelectListItem> Categories { get; set; }
}
EF Model:-
public class TR_ADVERTISEMENT
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int AdvID { get; set; }
[Required]
[DisplayName("Sub Category")]
public int SubCatID { get; set; }
public int CurrencyID { get; set; }
[DisplayName("Price on request")]
public bool IsPriceOnRequest { get; set; }
[DisplayName("Posted Date")]
[DisplayFormat (DataFormatString="{0:dd-MM-yyyy}")]
public Nullable<System.DateTime> CreatedDate { get; set; }
public Nullable<System.DateTime> ModifiedDate { get; set; }
}
On the save button click, i have to save the data into the tr_advertisement table using the EF model.
Please suggest the solution to this problem.
It is the model type you are passing into your Create ActionMethod.
public ActionResult Create(TR_ADVERTISEMENT tr_advert)
should be
public ActionResult Create(CreateAdvertVM tr_advert)
I am assuming that if your model is not valid, you are passing it back further down in your action result (which you are not showing), such as
Return View(tr_advert)
But, you are passing the wrong model type at that point for that view.
EDIT
I would also update your view model so that instead of inheriting from the EF class, simply include the EF class as a property.
public class CreateAdvertVM
{
[DisplayName("Category")]
[Required]
public int? SelectedCategoryId { get; set; }
public IEnumerable<SelectListItem> Categories { get; set; }
public TR_ADVERTISEMENT tr_advert{get;set;}
}
This will make it so that your save code in the Create method can still be used with only minor modifications
[HttpPost]
public ActionResult Create(CreateAdvertVM model)
{
if (ModelState.IsValid)
{
model.tr_advert.CreatedDate = model.tr_advert.ModifiedDate = DateTime.Now;
if (model.tr_advert.IsPriceOnRequest)
{
model.tr_advert.CurrencyID = 0;
model.tr_advert.Price = 0;
}
db.ADVERTISEMENT.Add(model.tr_advert);
db.SaveChanges();
return RedirectToAction("Index");
}

ASP MVC table foreign key configuration

public class Movies
{
[Key]
public int movieID { get; set; }
public String Title { get; set; }
public virtual Genres parentGenre { get; set; }
}
public class Genres
{
public Genres()
{
this.movies = new HashSet<Movies>();
}
[Key]
public int genreId { get; set; }
public String genreName { get; set; }
public ICollection<Movies> movies { get; set; }
}
I can successfully create a DropDownList on my addmovie view page by reading in the Genres table.
But when I try submit the page, it states "The value '10' is invalid." This leads me to believe that I am trying to put an Int (from the dropdownlist value) into an object (from the "Genres parentGenre").
Have I set up the "Genres parentGenre" to "genreId" foreign key relationship correctly to where it should only want a 'number' for the "parentGenre" field, or does it expect more?
added:
controller:
[HttpPost]
public ActionResult Create(Movies movies)
{
if (ModelState.IsValid)
{
db.Movies.Add(movies);
db.SaveChanges();
return RedirectToAction("Index");
}
IEnumerable<SelectListItem> items = db.Genres
.Select(c => new SelectListItem
{
Value = SqlFunctions.StringConvert((double)c.genreId).Trim(),
Text = c.genreName
});
ViewBag.genreId = items;
}
view:
<div class="editor-label">
#Html.LabelFor(model => model.parentGenre.genreName)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.parentGenre,(IEnumerable<SelectListItem>)ViewBag.genreId)
#Html.ValidationMessageFor(model => model.parentGenre)
In lieu of your comments, here's what I'd recommend:
Models:
public class Movie
{
[Key]
public int MovieID { get; set; }
public String Title { get; set; }
public int GenreId { get; set; }
public virtual Genre Genre { get; set; }
}
public class Genre
{
public Genre()
{
this.movies = new HashSet<Movies>();
}
[Key]
public int GenreId { get; set; }
public String Name { get; set; }
public ICollection<Movies> Movies { get; set; }
}
Controller:
[HttpPost]
public ActionResult Create(Movie Movie)
{
if (ModelState.IsValid)
{
db.Movies.Add(Movie);
db.SaveChanges();
return RedirectToAction("Index");
}
IEnumerable<SelectListItem> items = db.Genres
.Select(c => new SelectListItem
{
Value = SqlFunctions.StringConvert((double)c.GenreId).Trim(),
Text = c.Name
});
ViewBag.genreId = items;
}
View:
<div class="editor-label">
#Html.LabelFor(model => model.Genre.Name)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.Genre.Id,(IEnumerable<SelectListItem>)ViewBag.genreId)
#Html.ValidationMessageFor(model => model.Genre)
</div>
If I'm reading what you're doing correctly, that should work. I haven't done much with EF though, so I can't be 100% certain.

Resources