0

Im trying to get a drop-down list to work for users who are being graded. Each user can have multiple gradings. So when i create a new grade i want a drop-down to specify which use who will be receiving the grade.

I keep getting:

There is no ViewData item of type 'IEnumerable' that has the key 'GradingId'.

I've looked at many other questions but i cant work out what i need to change in my controller, view or models.

GradingController.cs

 public ActionResult Create()
        {
            return View();
        }

        // POST: Gradings/Create
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create([Bind(Include = "GradingId,Eye,Cheek,Mouth,RestSymmetryTotal,RestSymmetryScore,VolForeheadWrinkle,VolGentleEyeClosure,VolOpenMouthSmile,VolSnarl,VolLipPucker,VolSymmetryTotal,VolSymmetryScore,SynForeheadWrinkle,SynGentleEyeClosure,SynOpenMouthSmile,SynSnarl,SynLipPucker,SynkinesisScore,CompositeScore")] Grading grading)
        {
            if (ModelState.IsValid)
            {
                grading.GradeDate = DateTime.Now;
                db.Gradings.Add(grading);
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            ViewBag.GradingId = new SelectList(db.Gradings, "GradingId", "CodeName");
            return View(grading);
        }

        // GET: Gradings/Edit/5
        public ActionResult Edit(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Grading grading = db.Gradings.Find(id);
            if (grading == null)
            {
                return HttpNotFound();
            }
            ViewBag.GradingId = new SelectList(db.Gradings, "GradingId", "CodeName");
            return View(grading);
        }

        // POST: Gradings/Edit/5
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit([Bind(Include = "GradingId,Eye,Cheek,Mouth,RestSymmetryTotal,RestSymmetryScore,VolForeheadWrinkle,VolGentleEyeClosure,VolOpenMouthSmile,VolSnarl,VolLipPucker,VolSymmetryTotal,VolSymmetryScore,SynForeheadWrinkle,SynGentleEyeClosure,SynOpenMouthSmile,SynSnarl,SynLipPucker,SynkinesisScore,CompositeScore")] Grading grading)
        {
            if (ModelState.IsValid)
            {
                db.Entry(grading).State = EntityState.Modified;
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            ViewBag.GradingId = new SelectList(db.Gradings, "GradingId", "CodeName");
            return View(grading);
        }

Create.cshtml (View)

@model FaceToFace.Model.Grading

    <h2>Create</h2>


    @using (Html.BeginForm())
    {
        @Html.AntiForgeryToken()

        <div class="editor-label">
            @Html.LabelFor(model => model.User.CodeName, "User Name")
        </div>
        <div class="editor-field">
            @Html.DropDownList("GradingId", String.Empty)
        </div>
            <div class="form-group">
                <div class="col-md-offset-2 col-md-10">
                    <input type="submit" value="Create" class="btn btn-default" />
                </div>
            </div>
        </div>
    }

grading.cs (model)

namespace FaceToFace.Model
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Data.Entity.Spatial;

    [Table("Grading")]
    public partial class Grading
    {
public int? User_UserID { get; set; }
        public virtual User User { get; set; }

        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int GradingId { get; set; }

        public DateTime GradeDate { get; set; }
        public DateTime GradeEditDate { get; set; }
    }
}

User.cs (model)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FaceToFace.Model
{
    public class User
    {
      public virtual ICollection<Grading> UserGradings { get; set; }   
    }
}
Nilmag
  • 553
  • 6
  • 26

3 Answers3

4

You can avoid issues like this by using a Viewmodel. Darin Dimitrov explains better than I do why you should use one.

You get Intellisense and you can use the strongly typed versions of the Html helpers inside your views. You also get a refactor friendly code and no longer rely on magic strings. Also it is clear where the information is coming from to a given view by only looking at the view model that this view is strongly typed to.

For your create page, an appropriate Viewmodel can be created;

public class CreateGradeViewModel {
    Grading Grading { get; set; }
    IEnumerable<Grading> Gradings { get; set; }
}

Using that model for your view, you can then pass your dropdown collections as part of the model for the view. In this example, a domain object is included as part of the view model. If you require more control over this, you can use the properties of your domain model instead of a domain object, which would allow you to use data annotations. You would then need a mapper to map the object back to your domain type.

All you need to change in your controller is the GET method to instantiate the model with your dropdown values, and the POST method to accept the view model and act on it accordingly.

Community
  • 1
  • 1
Inspector Squirrel
  • 2,548
  • 2
  • 27
  • 38
  • Thankyou, would this make adding multiple grades to each user easier? as of right now i am unable to do that. I can only make one grade as a whole even if i assign it to new users, which it doesn't seem to be recognizing. – Nilmag Feb 25 '15 at 16:22
  • I believe im using model first, but i dont actually know how i could find out which one im using. Sorry for my ignorance. – Nilmag Feb 26 '15 at 11:01
  • Oh, never done it like that before. Basically I'm guessing you have a direct link between `User` and `Grade`? You'll want to add a link table between them that contains a foreign key for the `User` and a foreign key for the `Grade`. That will make it easier to have multiple grades per user, yes. – Inspector Squirrel Feb 26 '15 at 11:08
  • thankyou, and what would i have to do if it was EF or DB as im unsure which one i am using. – Nilmag Feb 26 '15 at 15:35
  • EF is an ORM which maps database records to objects in your application. The three most common ways of using EF are code first, model first and database first. Regardless of which you use, you will usually end up in roughly the same place with regards to generated files. – Inspector Squirrel Feb 26 '15 at 15:39
  • Using DB first, you'd just add the table yourself, using code first you'd just code the objects to contain collections so that it'd figure that out itself, and with model first you just add the table to the model. :) – Inspector Squirrel Feb 26 '15 at 15:39
  • Thanks man it's really appreciated. I'll give it a go! :) Im getting an error now which is : **Cannot insert the value NULL into column 'GradingId', table 'F2FData.dbo.Grading'; column does not allow nulls. INSERT fails.\r\nThe statement has been terminated** im guessing that has something to do with it. – Nilmag Feb 26 '15 at 15:47
  • You probably need to make sure you specify that that column is an identity so that it can auto-increment :) – Inspector Squirrel Feb 26 '15 at 16:02
  • I forgot to thank you for your help. I changed the Seed and Increment of Grading in SQL manager and then changed the[DatabaseGenerated(DatabaseGeneratedOption.None)] from None to Identity. Thanks for your help on that. – Nilmag Mar 02 '15 at 12:36
1

In Create get action you are not setting ViewBag.GradingId with the SelectList which is causing error in View:

public ActionResult Create()
{
        ViewBag.GradingId = new SelectList(db.Gradings, "GradingId", "CodeName");
        return View();
}
Ehsan Sajjad
  • 61,834
  • 16
  • 105
  • 160
  • This needs to be done in *both* the GET and POST actions. Otherwise, if there's an error that the user must correct, the drop down will be empty after redisplaying the form. – Chris Pratt Feb 24 '15 at 18:52
  • Whoops. The timing of that looked bad. That's not a reason for the downvote. I didn't downvote you. – Chris Pratt Feb 24 '15 at 18:52
  • @ChrisPratt OP is doing it in all other actions exception Create Get action – Ehsan Sajjad Feb 25 '15 at 04:37
1

Although it's not part of your question, you're going to run into an entirely different problem once you get this operational. The name of your ViewBag item cannot be the same as the name of your property. Otherwise, the selected value will never be selected as the value in ViewBag will override the value on your model.

Name your select list something like GradingIdChoices, not GradingId to disambiguate it.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444