ASP.net MVC DropLownList db.SaveChanges not saving selection - asp.net-mvc-3

I have looked through a ton of tutorials and suggestions on how to work with DropDownList in MVC. I was able to get most of it working, but the selected item is not saving into the database. I am using MVC 3 and Razor for the view.
My DropDownList is getting created with the correct values and good looking HTML. When I set a breakpoint, I can see the correct selected item ID in the model getting sent to controller. When the view goes back to the index, the DropDownList value is not set. The other values save just fine.
Here are the related views. The DropDownList is displaying a list of ColorModel names as text with the ID as the value.
public class ItemModel
{
[Key]
public int ItemID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ColorModel Color { get; set; }
}
public class ItemEditViewModel
{
public int ItemID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int ColorID { get; set; }
public IEnumerable<SelectListItem> Colors { get; set; }
}
public class ColorModel
{
[Key]
public int ColorID { get; set; }
public string Name { get; set; }
public virtual IList<ItemModel> Items { get; set; }
}
Here are the controller actions.
public ActionResult Edit(int id)
{
ItemModel itemmodel = db.Items.Find(id);
ItemEditViewModel itemEditModel;
itemEditModel = new ItemEditViewModel();
itemEditModel.ItemID = itemmodel.ItemID;
if (itemmodel.Color != null) {
itemEditModel.ColorID = itemmodel.Color.ColorID;
}
itemEditModel.Description = itemmodel.Description;
itemEditModel.Name = itemmodel.Name;
itemEditModel.Colors = db.Colors
.ToList()
.Select(x => new SelectListItem
{
Text = x.Name,
Value = x.ColorID.ToString()
});
return View(itemEditModel);
}
[HttpPost]
public ActionResult Edit(ItemEditViewModel itemEditModel)
{
if (ModelState.IsValid)
{
ItemModel itemmodel;
itemmodel = new ItemModel();
itemmodel.ItemID = itemEditModel.ItemID;
itemmodel.Color = db.Colors.Find(itemEditModel.ColorID);
itemmodel.Description = itemEditModel.Description;
itemmodel.Name = itemEditModel.Name;
db.Entry(itemmodel).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(itemEditModel);
}
The view has this for the DropDownList, and the others are just EditorFor().
#Html.DropDownListFor(model => model.ColorID, Model.Colors, "Select a Color")
When I set the breakpoint on the db.Color.Find(...) line, I show this in the Locals window for itemmodel.Color:
{System.Data.Entity.DynamicProxies.ColorModel_0EB80C07207CA5D88E1A745B3B1293D3142FE2E644A1A5202B90E5D2DAF7C2BB}
When I expand that line, I can see the ColorID that I chose from the dropdown box, but it does not save into the database.

You do not need to set the whole Color object. Just set the ColorId property.
Change
itemmodel.Color = db.Colors.Find(itemEditModel.ColorID);
To
itemmodel.ColorId = itemEditModel.ColorID;
Edit
Note that your database does not store the whole object. The Color object in ItemModel is just a convenient way to access the ColorModel entity that is assosiated by a foreign key.
According to convention, the name of the foreign key property should be ColorId. Add this int property in your ItemModel class.

Related

Populating a dropdownlist with values

Hello I'm trying to make my dropdownlist to work but it seems harder than expected. I have 3 domain classes Member, Rental and Movie. My Idea was to make a dropdownlist that will show a specific users rented movies and when I select a movie in the dropdownlist and submit it I will get back the selected movie and I can set bool IsInStock to true.
So I made a viewmodel and a controller action but would like some help how to go forward with this. Now I get a dropdownlist with the users "Jan" rented movies but when I klick submit I would like to get the values back in order to set the IsInStock to true. I know I will need method to handle the POST values but I'm trying to make this work first.
public class Member
{
public virtual int MemberId { get; set; }
public virtual string LastName { get; set; }
public virtual List<Rental> Rentals { get; set; }
}
public class Rental
{
public virtual int RentalId { get; set; }
public virtual int MovieId { get; set; }
public virtual Movie Movie { get; set; }
public virtual int MemberId { get; set; }
public virtual Member Member { get; set; }
}
public class Movie
{
public virtual int MovieId { get; set; }
public virtual string Name { get; set; }
public virtual bool IsInStock { get; set; }
}
public class RentalsViewModel
{
// Need something here.
public IEnumerable<SelectListItem> RentedMovies { get; set; }
}
public ActionResult ReturnMovie()
{
var rentedmovies = db.Rentals.Where(r => r.Member.Name == "Jan").ToList();
var model = new RentalsViewModel()
{
RentedMovies = rentedmovies.Select(x => new SelectListItem
{
Value = x.MovieId.ToString(),
Text = x.Movie.Name
})
};
return View(model);
}
// In the View
#using (Html.BeginForm())
{
#Html.DropDownListFor(x => x.RentedMovies, //Something here);
<p>
<input type="submit" value="Save" />
</p>
}
First parameter here:
#Html.DropDownListFor(x => x.RentedMovies, //Something here);
must be int, because you will send this value to the server:
public virtual int MovieId { get; set; }
So, your example could look like:
#Html.DropDownListFor(x => x.RentedMovieId, Model.RentedMovies);
(Add property RentedMovieId to RentalsViewModel)

How to Prevent some model attributes from Update?

I'm using Scaffolding in ASP.Net, I've a Model called "Page" which has attributes as follows
public class Page
{
private DateTime _Created_at = DateTime.Now;
private bool _IsActive = true;
public int ID { get; set; }
public string Code { get; set; }
[Required]
[DisplayName("Parent Code")]
public string ParentCode { get; set; }
[Required]
public string Title { get; set; }
************
}
Here, During Create Method, I'm updating Code attributes as follows
public ActionResult Create(Page page)
{
if (ModelState.IsValid)
{
page.Code = page.Url.Replace(" ", string.Empty);
page.IsActive = true;
db.Pages.Add(page);
db.SaveChanges();
return RedirectToAction("Details", new { id = page.ID });
}
return View(page);
}
Now, Problem is, I don't want this Code value change during Update method, I'm not included it in Edit form. But still it's updating 'NULL' value if I update.
I tried [Bind(Exclude = "Code")] for Page class, But no use.
You need a hidden field for code in your edit view. Use #Html.HiddenFor(model => model.Code).

Binding ListBox with a model in MVC3

My model is
public class SiteConfig
{
public SiteConfig()
{
}
public int IdSiteConfig { get; set; }
public string Name { get; set; }
public byte[] SiteLogo { get; set; }
public string Brands { get; set; }
public string LinkColour { get; set; }
public IEnumerable<SiteBrand> SiteBrands { get; set; }
}
and
public class SiteBrand
{
public int Id { get; set; }
public int SiteId { get; set; }
public int BrandId { get; set; }
public Brand Brand { get; set; }
public SiteConfig SiteConfig { get; set; }
}
public class Brand
{
public int BrandId { get; set; }
public string Name { get; set; }
public IEnumerable<SiteBrand> SiteBrands { get; set; }
}
I am following Data Base first approach. Each SiteConfig record can contain one or more Brand. So Brand is saving to another table called SiteBrand.
SiteBrand contains the forign key reference to both SiteConfig(on IdSiteConfig) and Brand(BrandId).
When I am creating a SiteConfig I want to display all the available Brand as list box where user can select one or many record(may not select any brand).
But when I bind my view with the model how can I bind my list box to the list of brands and when view is posted how can I get the selected brands.
And I have to save the SiteConfig object to database with the selected Items. And this is my DB diagram.
This is my DAL which saves to db.
public SiteConfig Add(SiteConfig item)
{
var siteConfig = new Entities.SiteConfig
{
Name = item.Name,
LinkColour = item.LinkColour,
SiteBrands = (from config in item.SiteBrands
select new SiteBrand {BrandId = config.BrandId, SiteId = config.SiteId}).
ToList()
};
_dbContext.SiteConfigs.Add(siteConfig);
_dbContext.SaveChanges();
return item;
}
Can somebody advide how to bind the list box and get the selected items.
Thanks.
Add a new Property to your SiteConfig ViewModel of type string array. We will use this to get the Selected item from the Listbox when user posts this form.
public class SiteConfig
{
//Other properties here
public string[] SelectedBrands { get; set; } // new proeprty
public IEnumerable<SiteBrand> SiteBrands { get; set; }
}
In your GET action method, Get a list of SiteBrands and assign to the SiteBrands property of the SiteConfig ViewModel object
public ActionResult CreateSiteConfig()
{
var vm = new SiteConfig();
vm.SiteBrands = GetSiteBrands();
return View(vm);
}
For demo purposes, I just hard coded the method. When you implement this, you may get the Data From your Data Access layer.
public IList<SiteBrand> GetSiteBrands()
{
List<SiteBrand> brands = new List<SiteBrand>();
brands.Add(new SiteBrand { Brand = new Brand { BrandId = 3, Name = "Nike" } });
brands.Add(new SiteBrand { Brand = new Brand { BrandId = 4, Name = "Reebok" } });
brands.Add(new SiteBrand { Brand = new Brand { BrandId = 5, Name = "Addidas" } });
brands.Add(new SiteBrand { Brand = new Brand { BrandId = 6, Name = "LG" } });
return brands;
}
Now in your View, which is strongly typed to SiteConfig ViewModel,
#model SiteConfig
<h2>Create Site Config</h2>
#using (Html.BeginForm())
{
#Html.ListBoxFor(s => s.SelectedBrands,
new SelectList(Model.SiteBrands, "Brand.BrandId", "Brand.Name"))
<input type="submit" value="Create" />
}
Now when user posts this form, you will get the Selected Items value in the SelectedBrands property of the ViewModel
[HttpPost]
public ActionResult CreateSiteConfig(SiteConfig model)
{
if (ModelState.IsValid)
{
string[] items = model.SelectedBrands;
//check items now
//do your further things and follow PRG pattern as needed
}
model.SiteBrands = GetBrands();
return View(model);
}
You can have a "ViewModel" that has both the site and brand model in it. Then you can bind your view to that model. This would allow you to bind any part of the view to any part of any of the underlying models.
public class siteViewModel{
public SiteConfig x;
public Brand b; //Fill this with all the available brands
}
Of course you can include any other information your view might need (reduces the need of ViewBag as well).

RequiredIf validation in asp.net mvc3

I had a view model in that viewmodel i created two different instances using same model like this:
public ClaimViewModel()
{
engineClaim = new ClaimModel();
boatClaim = new ClaimModel();
}
public ClaimModel engineClaim { get; set; }
public ClaimModel boatClaim { get; set; }
I had properties in ClaimModel like this:
[Required]
public string Complaint { get; set; }
[RequiredIf("isEngineClaim", true , ErrorMessage = "Required")]
public string Cause { get; set; }
[Required]
public string Correction { get; set; }
public bool isEngineClaim { get; set; }
And in controller i am loading the index page like this
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
ClaimViewModel claim = new ClaimViewModel();
claim.engineClaim = new Models.ClaimModel();
claim.engineClaim.isEngineClaim = true;
claim.boatClaim = new Models.ClaimModel();
claim.boatClaim.isEngineClaim = false;
return View("Index", claim);
}
Now my problem is the requiredif validation is not working though the 'isEngineClaim' property is different for two instances. I am following this link
And moreover i had placed the hidden field of 'isEngineClaim' in my view also. But the requiredifvalidation is not working can anyone suggest me the solution.

Asp.Net MVC3 - How create Dynamic DropDownList

I found many articles on this but still I don´t know how exactly to do this. I am trying to create my own blog engine, I have View for create article (I am using EF and Code first) and now I must fill number of category in which article should be add but I want to change it to dropdownlist with names of categories. My model looks this:
public class Article
{
public int ArticleID { get; set; }
[Required]
public string Title { get; set; }
[Required]
public int CategoryID { get; set; }
public DateTime Date { get; set; }
[Required()]
[DataType(DataType.MultilineText)]
[AllowHtml]
public string Text { get; set; }
public virtual Category Category { get; set; }
public IEnumerable<SelectListItem> Categories { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
public class Category
{
public int CategoryID { get; set; }
[Required]
public string Name { get; set; }
public virtual ICollection<Article> Articles { get; set; }
}
I know I must use Enum (or I think) but I am not exactly sure how. I don´t know which tutorial from that I found is best for me.
Edit:
Thanks for your answers but I found something else. I am trying this:
This is my model:
public class Article
{
[Key]
public int ArticleID { get; set; }
[Display(Name = "Title")]
[StringLength(30, MinimumLength = 5)]
[Required]
public string Title { get; set; }
public DateTime Date { get; set; }
public int CategoryID { get; set; }
[Required()]
[DataType(DataType.MultilineText)]
[AllowHtml]
public string Text { get; set; }
public Category Category { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
public IEnumerable<Category> Categories { get; set; }
}
public class Category
{
[Key]
public int CategoryId { get; set; }
[Required]
public string CategoryName { get; set; }
public virtual ICollection<Article> Articles { get; set; }
}
This is my controller to create article:
public ActionResult Vytvorit()
{
IEnumerable<Category> categories = GetCaregories();
var view = View(new Article() { Categories = categories });
view.TempData.Add("Action", "Create");
return view;
}
private static IEnumerable<Category> GetCaregories()
{
IEnumerable<Category> categories;
using (BlogDBContext context = new BlogDBContext())
{
categories = (from one in context.Categories
orderby one.CategoryName
select one).ToList();
}
return categories;
}
private Category GetCategory(int categoryID)
{
return db.Categories.Find(categoryID);
}
//
// POST: /Clanky/Vytvorit
[HttpPost]
public ActionResult Vytvorit(Article newArticle)
{
try
{
if (newArticle.CategoryID > 0)
{
newArticle.Category = GetCategory(newArticle.CategoryID);
}
if (TryValidateModel(newArticle))
{
db.Articles.Add(newArticle);
db.SaveChanges();
return RedirectToAction("Index");
}
else
{
newArticle.Categories = GetCaregories();
var view = View(newArticle);
view.TempData.Add("Action", "Create");
return view;
}
}
catch
{
return View();
}
}
And this is part of my view:
#Html.DropDownListFor(model => model.CategoryID, new SelectList(Model.Categories,"CategoryID","CategoryName"))
#Html.ValidationMessageFor(model => model.CategoryID)
I have problem with NullReferenceExeption but I don´t know why. Can I do it this way? It looks very easy for me.
Your model seems quite strange. It contains properties such as CategoryID and Category which seem redundant. It also contains a SelectListItem collection property called Categories. So, is this a model or a view model? It looks quite messed up. Let's assume it's a model. In this case it would more likely look something like this:
public class Article
{
public int ArticleID { get; set; }
[Required]
public string Title { get; set; }
public DateTime Date { get; set; }
[Required()]
[DataType(DataType.MultilineText)]
[AllowHtml]
public string Text { get; set; }
public virtual Category Category { get; set; }
public IEnumerable<Category> Categories { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
public class Category
{
public int CategoryID { get; set; }
[Required]
public string Name { get; set; }
public virtual ICollection<Article> Articles { get; set; }
}
Now that the model is clear we could define a view model which will be passed to the view. A view model is a class which is specifically designed for the view. So depending on what you intend to put in this view you define it in this view model. So far you have talked only about a drop down, so let's do it:
public class ArticleViewModel
{
public int SelectedCategoryId { get; set; }
public IEnumerable<SelectListItem> Categories { get; set; }
}
and then we have a controller:
public class ArticlesController: Controller
{
private readonly IArticlesRepository _repository;
public ArticlesController(IArticlesRepository repository)
{
_repository = repository;
}
public ActionResult Index()
{
Article article = _repository.GetArticle();
ArticleViewModel viewModel = Mapper.Map<Article, ArticleViewModel>(article);
return View(viewModel);
}
}
So the controller uses a repository to fetch the model, maps it to a view model (in this example I use AutoMapper) and passes the view model to the view which will take care of showing it:
#model AppName.Models.ArticleViewModel
#using (Html.BeginForm())
{
#Html.DropDownListFor(
x => x.SelectedCategoryId,
new SelectList(Model.Categories, "Value", "Text"),
"-- Select category --"
)
<input type="submit" value="OK" />
}
I have gone through this as well and I have to agree that at first it seems odd (In my explanation I'm assuming you want to select one category only, but the process is very similar for a multi select).
Basically you need to perform 3 steps:
1:
You need two properties on your viewmodel
One will hold the selected category id (required for postback) and the other will a SelectList with all possible categories:
public class Article
{
public int ArticleID { get; set; }
public int CategoryID { get; set; }
public SelectList Categories { get; set; }
}
2:
Also before passing the viewmodel on to the view you need to initialize the SelectList (Best practivce is to prepare as much as possible before passing a model into the view):
new SelectList(allCategories, "CategoryID", "Name", selectedCategoryID)
3:
In the view you need to add a ListBox for the CategoryID property, but using the Categories property too fill the ListBox with values:
#Html.ListBoxFor(model => model.CategoryID , Model.Categories)
Thats it! In the post back action of the controller you will have the CategoryID set. You can do whatever you need to from there to persist things in your db.

Resources