MVC 5 EnumDropDownListFor implementation - c#

I need to implement a dropdown box for the project. I used enum class to populate it, here is what i have
public class Media
{
public int Id { get; set; }
public string title { get; set; }
public string description { get; set; }
public string tag { get; set; }
public TagsEnum TagsEnum { get; set; }
}
And enum class
public enum TagsEnum
{
Ecomomy,
Latest,
LocalNews,
Interesting,
WorldNews
}
Create method
public ActionResult Create([Bind(Include = "Id,title,description,body,VideoLink, Source,tags")] Media media, HttpPostedFileBase file, int id = 0)
{
if (ModelState.IsValid)
{
if (file != null)
{
file.SaveAs(HttpContext.Server.MapPath("~/Images/")
+ file.FileName);
media.ImagePath = file.FileName;
}
db.Medias.Add(media);
db.SaveChanges();
return RedirectToAction("Index");
}
And here is the problem, on my Create View i have dropdown list working fine, here is the code:
<div class="form-group">
#Html.LabelFor(model => model.tags, "News Category", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EnumDropDownListFor(model => model.TagsEnum)
#Html.ValidationMessageFor(model => model.tags)
</div>
</div>
But when i open a view page MediaMain were i have a list of media objects, even if i select different categories from dorpdown list i only have 1st one shown. Here is the code
foreach (var b in Model.media)
{
...
<h5>Category: <a href=#Url.Action("Media", new { id = b.Id })>#Html.Raw(b.TagsEnum)</a></h5>
...
}
What do i misunderstand here, please help. Thanks.

The problem is with how you are binding the properties of Media in you controller action result. The Media class has a property public TagsEnum TagsEnum { get; set; } which is bound to the drop down list but in the action result you are not binding it:
[Bind(Include = "Id,title,description,body,VideoLink, Source,tags")] Media media
This causes that the TagsEnum value will not be stored in the database so when you retrieve it in the list of media, it simply returns the first value of the enum. You are using a bunch of other fields like VideoLink or tags which are not properties of Media at all. Those are ignored, but if you want the TagsEnum to be assigned to the media variable, you must add it like this:
[Bind(Include = "Id,title,description,body,VideoLink, Source,tags, TagsEnum")] Media media

Related

ASP.NET MVC Form POST is very slow for large nested checkbox list

I'm having a problem with my ASP.NET MVC web application where it takes ~30 seconds for my ViewModel to hit my controller when the form is posted. I'm guessing it has to do with the default Model Binder because of this.
[HttpPost]
public ActionResult Edit(ByActivityEditViewModel viewModel)
{
if (ModelState.IsValid) // Takes ~30 seconds before even hitting this
{
My View is a series of nested checkboxes of Group parents and User children. The same User may be listed under multiple Groups. I'm using an EditorFor to generate the checkboxes for the ViewModel.
View's Editor Template Call: #Html.EditorFor(model => model.Groups)
Editor Template:
#model MyProject.Models.Group
#Html.HiddenFor(model => model.Guid)
#Html.HiddenFor(model => model.Name)
#Html.CheckBoxFor(model => model.IsAllowed, new { #class = Model.Guid.ToString(), #style = "margin-right:5px; cursor:pointer;" }) #Html.LabelFor(model => model.IsAllowed, Model.Name, new { #class = "build-checkbox-label", #style = "font-weight:normal; margin-top:-2px;" })
#if (Model.Users.Any())
{
<ul style="list-style:none;">
#for (int i = 0; i < Model.Users.Count; i++)
{
<li>
#Html.HiddenFor(model => Model.Users[i].Guid)
#Html.HiddenFor(model => Model.Users[i].Name)
#Html.CheckBoxFor(model => Model.Users[i].IsAllowed, new { #class = Model.Users[i].Guid.ToString(), #style = "margin-right:5px; cursor:pointer;" }) #Html.LabelFor(model => Model.Users[i].IsAllowed, Model.Users[i].Name, new { #class = "build-checkbox-label", #style = "font-weight:normal; margin-top:-2px;" })
</li>
}
</ul>
}
ViewModel:
public class ByActivityEditViewModel
{
public int ActivityId { get; set; }
public string Path { get; set; }
public IList<Group> Groups { get; set; } = new List<Group>();
}
public class Group
{
public Guid? Guid { get; set; }
public string Name { get; set; }
public string DistinguishedName { get; set; }
public string SamAccountName { get; set; }
public string DisplayName { get; set; }
public bool IsAllowed { get; set; }
public List<User> Users { get; set; } = new List<User>();
}
public class User
{
public Guid? Guid { get; set; }
public string Name { get; set; }
public string DistinguishedName { get; set; }
public string SamAccountName { get; set; }
public string DisplayName { get; set; }
public bool IsAllowed { get; set; }
public bool IsUserChecked { get; set; }
}
ModelState:
The Model State contains the Guids, Names, and IsAllowed values. I believe processing this is the slow part.
There ends up being:
64 Groups
853 Users
The user can be part of more than one group.
I've tried:
Using string for Guid and Parsing it later.
Removing BeginCollectionItem package I was using.
Yelling at my monitor.
Any suggestions, information, or a workaround would be appreciated.
Jquery/js functions have done this for me previously a couple times, when handling large number of controls and i have banged my head trying to figure it out. Now I know where to look first.
So it is not clear from the post what is the fix unless you read the comments. So the answer is to add #{ Html.EnableClientValidation(false); } before #using (Html.BeginForm())
This turns off default client side validation and makes you form post faster to the server. Ofcourse if you need client side validation, then this is not the solution. In my case, my form had 200 rows with checkboxes and I didn't need the client side validation. So this solution worked great for me.

Passing multiple class objects to razor view through controller in mvc, asp.net

I am trying to perform CRUD operations on two classes PurchaseDetail and ItemDetail. Made PurchaseDetail a base class and ItemDetail as child class. So on a single razor view I am passing the child class object. Now when I'm performing CRUD operation on child class everything works fine. But when trying the same with base class I'm getting null object while adding data by calling a create(ItemDetail obj), on HttpPost.
Any solution for this? Is it a good practice to pass multiple classes to a view by using inheritance?
Edit
Elaboration:
public class PurchaseDetail
{
public int ID { get; set; }
[Required]
[Display(Name = "PPRF Name")]
public string pprfName { get; set; }
public int DepartmentID { get; set; }
[Display(Name ="Department")]
public string DepartmentName { get; set; }
public int DepartmentSerial { get; set; }
public List<SelectListItem> MaterialTypes { get; set; }
Above class is my base class with few properties. Then Below is my child class.
public class ItemDetail: PurchaseDetail
{
public int ItemID { get; set; }
public int ItemCategoryID { get; set; }
public string ItemCategoryName { get; set; }
public int ProductID { get; set; }
If I would take both in different pages there was not any problem. But I want to pass both class onto same view so I went for Inheritance. I am popping up form for ItemDetail from the same view, to add an item.
#model AIIMSINTRANET.Models.ItemDetail
Firstly I am inserting ItemDetail by using Ajax call. Here things are working fine.
public JsonResult Add(ItemDetail item)
{
//Insert Logic
}
After this I want to insert PurchaseDetail in it's respective table. Since the view was same so every fields like TextBox, Dropdowns were filled while calling this method.
public ActionResult Create()
{
ItemDetail obj = new ItemDetail();
obj.DeptIndentNo = GetIndentNumber2();
obj.IndentDate = DateTime.Now.Date;
obj.MaterialTypes = PurchaseProposalDO.PopulateMaterialType();
//Initialized all other fields
}
Even fields like DropDown of ItemDetail class got populated during above function call. Now when I am submitting the form to Insert PurchaseDetail, it calls below function:
[HttpPost]
public ActionResult Create(ItemDetail obj)
{
// Insert Logic Here
}
Now here is my problem. Why am I getting obj as null ?
Even when I am inputting everything.
<div class="form-group">
#Html.LabelFor(model =>model.pprfName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model =>model.pprfName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.pprfName, "", new { #class = "text-danger" })
</div>
</div>
Please help.
Without seeing the class definitions in question it's hard to answer definitively, but it sounds like what you've got is an Order Header/Order Line type relationship. If that's the case, using inheritance is absolutely the wrong approach for solving this. You should use a view model that encapsulates the other two objects and pass that. Something like:
public class OrderViewModel
{
public PurchaseDetail PurchaseDetail { get; set; }
public ItemDetail ItemDetail { get; set; }
}
Or, if there could be many ItemDetails for a single PurchaseDetail:
public class OrderViewModel
{
public PurchaseDetail PurchaseDetail { get; set; }
public IEnumerable<ItemDetail> ItemDetails { get; set; }
}
Alternatively, you could have the view model pass only the properties of interest to the view. Again, without seeing the definition of the classes it's hard to be more specific.

update exception using multiple tables

Hi everyone so I am trying to create an application using asp.net mvc with a code first database that allows the users to be able to create a blog post with as many images as they wish.I am currently trying to have the image path in one table and the heading,body text in the other table along with a foreign key to the image path.So that I can create one post with multiple images. This is my first time using multiple tables and currently I am getting an error when it reaches this line context.SaveChanges(); in the save method when I am trying to create a post and save it to the db. Thank you for any help with this issue.
An exception of type 'System.Data.Entity.Infrastructure.DbUpdateException' occurred in EntityFramework.dll but was not handled in user code
Additional information: An error occurred while updating the entries. See the inner exception for details
I was able to get the program to work when I was using one table but it had this issue : https://imgur.com/a/lQQ3Q
Here is the database Diagram :http://imgur.com/a/iJZGx
Query that I tried to make but am not sure where to use in my code.
var query = db.PostModel.Where(x => x.PostID == PostId).Select(x => new
{
PostID = x.PostID,
ImageId = x.ImageModel.ImageId,
ImagePath = x.ImageModel.ImagePath,
Heading = x.PostModel.Heading,
PostBody = x.PostModel.PostBody
}).FirstOrDefault();
My program
View to Create posts
#model Crud.Models.PostModel
....
#using (Html.BeginForm("Create", "Home", null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<form action="" method="post" enctype="multipart/form-data">
#Html.LabelFor(model => model.ImageModel.ImagePath)
<input id="ImagePath" title="Upload a product image" multiple="multiple" type="file" name="files" />
#Html.LabelFor(model => model.Heading)
<input id="Heading" title="Heading" name="Heading" />
#Html.LabelFor(model => model.PostBody)
<input id="PostBody" title="PostBody" name="PostBody" />
<p><input type="submit" value="Create" /></p>
</form>
}
View to display posts
#model IEnumerable<Crud.Models.PostModel>
....
#foreach (var item in Model)
{
<div>#Html.DisplayFor(modelItem => item.Heading)</div>
<div>#Html.DisplayFor(modelItem => item.PostBody)</div>
<div><img class="img-thumbnail" width="150" height="150" src="/Img/#item.ImageModel.ImagePath" /></div>
}
Models
public partial class PostModel
{
[Key]
[HiddenInput(DisplayValue = false)]
public int PostID { get; set; }
public string Heading { get; set; }
public string PostBody { get; set; }
[ForeignKey("ImageModel")]
public int ImageId { get; set; }
public virtual ImageModel ImageModel { get; set; }
}
public class ImageModel
{
[Key]
public int ImageId { get; set; }
public string ImagePath { get; set; }
public string PostID { get; set; }
}
DBcontext
public class EFDbContext : DbContext
{
public DbSet<SchoolNewsModel> SchoolNews { get; set; }
public DbSet<PostModel> Posts { get; set; }
public DbSet<ImageModel> Images { get; set; }
}
Controller
public ViewResult Display()
{
return View(repository.Posts);
}
public ViewResult Create()
{
return View("Create", new PostModel());
}
[HttpPost]
public ActionResult Create(PostModel Image, IEnumerable<HttpPostedFileBase> files)
{
if (ModelState.IsValid)
{
foreach (var file in files)
{
PostModel post = new PostModel();
if (file.ContentLength > 0)
{
file.SaveAs(HttpContext.Server.MapPath("~/Img/") + file.FileName);
// post.ImagePath = file.FileName;
post.PostBody = post.PostBody;
post.Heading = post.Heading;
}
repository.Save(post);
}
}
return RedirectToAction("display");
}
public ViewResult PublicPostDisplay()
{
return View(repository.Posts);
}
Repository
public IEnumerable<PostModel> Posts
{
get { return context.Posts; }
}
public void Save(PostModel Image)
{
if (Image.PostID == 0)
{
context.Posts.Add(Image);
}
else
{
PostModel dbEntry = context.Posts.Find(Image.PostID);
if (dbEntry != null)
{
dbEntry.ImageModel.ImagePath = Image.ImageModel.ImagePath;
}
}
context.SaveChanges();
}
You need to include the full details of the error. Its the See the inner exception for details that will give you the relevant information. However that will probably not matter since your models and relationships are incorrect.
You want a PostModel to have multiple ImageModel so you need a one-many relationship and your PostModel needs have the following property
public virtual ICollection<ImageModel> Images { get; set; }
and delete the int ImageId and ImageModel ImageModel properties. In addition the ImageModel should contain public virtual PostModel Post { get; set; }
Your POST method to create a new PostModel then becomes
[HttpPost]
public ActionResult Create(PostModel post, IEnumerable<HttpPostedFileBase> files)
{
if (!ModelState.IsValid)
{
return View(post);
}
foreach (var file in files)
{
if (file.ContentLength > 0)
{
file.SaveAs(HttpContext.Server.MapPath("~/Img/") + file.FileName);
// Initialize a new ImageModel, set its properties and add it to the PostModel
ImageModel image = new ImageModel()
{
ImagePath = file.FileName
};
post.Images.Add(image);
}
}
repository.Save(post);
return RedirectToAction("display");
}
There are however multiple other issues with your code that you should address.
First, your view has nested forms which is invalid html and not
supported. You need to remove the inner <form> tag
Your editing data, so always use a view model (refer What is
ViewModel in
MVC?)
and the PostVM will include a property
IEnumerable<HttpPostedFileBase> Images and in the view, bind to it
using #Html.TextBoxFor(m => m.Images, new { type = "file", multiple
= "multiple" })
You have no validation at all, and your properties should include
validation attributes, for example, a [Required] attribute on
Heading and Body. The you need to include
#Html.ValidationMessageFor() for each property in the view.
You manual html for the inputs will not give you 2-way model binding
and prevent any client side validation. Always use the HtmlHelper
methods to generate form controls, e.g. #Html.TextBoxFor(..)
Do not save the image with just the file name (multiple users may
upload files with the same name and overwrite existing files. One
option is to use a Guid for the file name, and include a
additional property string DisplayName in ImageModel. Refer
this
answer
for an example of that approach.

MVC Dropdown option displayed as 'null' in debug mode

Afternoon Folks,
Im new to MVC 5 and C# and have a simple form with several fields and a dropdown box. I have used the CRUD method for entity framework and can successfully view and create new records in the system.
The only issue that I have is I have a 'Title' dropdown that links to the entity framework and populates these titles into a list. when I load my web page I can now see the titles available in the dropdown list, but when submitting the form, all the values but the 'Title' field are submitted into the database.
When I debug my program, this field regardless of what I select displays null.
I have followed the following tutorial to get this wo work and looked around the net but im struggeling to find a solution.
Link to tutorial
I have two tables in my model, one named 'Title' and the other named 'Client Record'.
As I have used the database first approach and not code first I have combined these two database models into one:
namespace EDT_Test.Models.ViewModels
{
public partial class Marie_Testing
{
[Display(Name = "Client Ref:")]
public int id { get; set; }
[Display(Name = "Created By:")]
public string CreatedBy { get; set; }
public List<Title> allTitles { get; set; }
[Required]
[Display(Name = "Surname:")]
public string Surname { get; set; }
[Display(Name = "Additional Surname:")]
public string Surname2 { get; set; }
[Required]
[Display(Name = "Forename:")]
public string Forename1 { get; set; }
[Display(Name = "Additional Forename:")]
public string Forename2 { get; set; }
The generated Entity Framework model looks like this:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace EDT_Test.Models
{
using System;
using System.Collections.Generic;
public partial class ClientRecord
{
public int id { get; set; }
public string CreatedBy { get; set; }
public string Title { get; set; }
public string Surname { get; set; }
public string Surname2 { get; set; }
public string Forename1 { get; set; }
public string Forename2 { get; set; }
}
}
The only difference for the Title field between the auto created model and Marie_Testing model is I have changed the Title field from a string to a list item.
My Create.cshtml holds a div for the Title dropdown that looks like this (This links to my model named Marie_Testing and not the auto generated ones created by the Entity Framework:
<div class="form-group">
#Html.LabelFor(model => model.allTitles, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<select id="titleid" name="titlename" class="form-control">
#foreach (var item in Model.allTitles)
{
<option value="#item.id">#item.Title1</option>
}
</select>
</div>
</div>
My code for the ClientRecordsController is:
// GET: ClientRecords/Create
public ActionResult Create()
{
////set the defaults (dropdown) of the page for the ceaton of a new record.
Marie_Testing vmPopulateData = new Marie_Testing();
List<Title> titles = (from t in db.Titles select t).ToList();
//List<Title> titles = Title.Select(t => new{t.id, t.Title}.ToString.ToList());
vmPopulateData.allTitles = titles;
return View(vmPopulateData);
}
// POST: ClientRecords/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "id,CreatedBy,Title,Surname,Surname2,Forename1,Forename2")] ClientRecord clientRecord)
{
if (ModelState.IsValid)
{
db.ClientRecords.Add(clientRecord);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(clientRecord);
}
Any help is much appreciated as I don't understand how can see the dropdown list in the web page but cannot seem to grab the selected value and post this to the database.
Regards
Betty B
Why not try the #Html.DropDownListFor?
Instead of this:
<select id="titleid" name="titlename" class="form-control">
#foreach (var item in Model.allTitles)
{
<option value="#item.id">#item.Title1</option>
}
</select>
try:
#Html.DropDownListFor(x => x.PropertyToBindTo, new SelectList(Model.allTitles, "id", "Title1"), new { #class = "form-control", id = "Title", name = "Title" })
Where x.PropertyToBindTo is whatever value that you need to get from that select list. Try it out... you may have to play with it a little in order to really understand how you need to work it.
You need to have a string field to hold the value of the selected from the dropdown so your view would change from
<select id="titleid" name="titlename" class="form-control">
to
<select id="Title" name="Title" class="form-control">
And you will also have a Title property on your view model like this
public string Title{get;set;}
You need to have a read on how MVC binds forms to models to get an understanding of why this happens.
Hope this helps..
well I prefer to use this
C#
List<Titles> oTitlesList = TitlesService.getTitles();
ViewBag.DropDownListBag = new SelectList(oTitlesList , "Id", "Name");
Razor
#Html.DropDownListFor(model => model.yourModelAtt, #ViewBag.DropDownListBag as SelectList, "Select.....", new { #class = "form-control" })

Two the same models in one view MVC

I am having one form in my website. The form contains information about two localizations. First localization called FROM and second called TO.
In my view I am using
#model DDP.Models.Localization
Here is my Localization entity:
public class Localization
{
[Key]
public int ID { get; set; }
[Required]
public string Province { get; set; }
[Required]
public string City { get; set; }
[Required]
public string PostalCode { get; set; }
public string StreetAdres { get; set; }
}
The problem is I want pass to my controller two instances of Localization like below:
public ActionResult AddRoute(Localization from, Localization to)
{
return View();
}
There is problem when I want to do it like below because I have two the same models.
#Html.TextBoxFor(model => model.City, new { #id = "cityFrom", #class = "form-control", #placeholder = "Miasto", #style = "margin-bottom: 10px;" })
Can somone show me right direction?
Thanks!
Make a viewModel hosting the two instances, and use that into your view.
namespace DDP.ViewModels
{
public class vm
{
Localization L1 {get; set;}
Localization L2 {get; set;}
}
}
and use it into your view like this:
public ActionResult AddRoute(Localization from, Localization to)
{
DDP.ViewModels.vm ret = new DDP.ViewModels.vm() { L1 = from, L2 = to};
return View(ret);
}
#model DDP.ViewModels.vm
#Html.TextBoxFor(model => model.L1.City, new { #id = "cityFrom", #class = "form-control", #placeholder = "Miasto", #style = "margin-bottom: 10px;" })
(I didn't actually test the code, I just typed it here into the editor maybe there's some mistake but hopefully it should put you onto the right way!)

Categories