9

I'm stuck creating a proper create/edit view in ASP.NET MVC5. I've got two models Dog and Human. A dog belongs to one Human. I'm trying to create a dropdown in the create and edit views for Dog that'll allow me to select a Human by name for that particular Dog. Here are my models:

Human:

public class Human
{
    public int ID { get; set; }
    public string Name { get; set; }
}

Dog:

public class Dog
{
    public int ID { get; set; }
    public string Name { get; set; }
    public Human Human { get; set; }
}

My create action:

// GET: /Dog/Create
public ActionResult Create()
{
    ViewBag.HumanSelection = db.Humen.Select(h => new SelectListItem
    {
        Value = h.ID.ToString(),
        Text = h.Name
    });
    return View();
}

And here is the relevant part of my view:

<div class="form-group">
    @Html.LabelFor(model => model.Human.Name, new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.DropDownListFor(model => model.Human, ViewBag.HumanSelection);
    </div>
</div>

I get the following error when I run this:

Compiler Error Message: CS1973: 'System.Web.Mvc.HtmlHelper<Test.Models.Dog>' has no applicable method named 'DropDownListFor' but appears to have an extension method by that name. Extension methods cannot be dynamically dispatched. Consider casting the dynamic arguments or calling the extension method without the extension method syntax.

I'm new to C# & the Entity framework. What am I doing wrong? Is there a way of doing this without manually querying the database? Something like the collection form helpers in Rails? I've followed a bunch of tutorials that are either old or too complicated for me to follow.

Prajjwal
  • 1,055
  • 2
  • 10
  • 17
  • 1
    I don't think it's clear by your question what the problem is. Are you receiving an error? – alan May 05 '14 at 14:21
  • The following link may be helpful for you.This works for me
    http://ilyasmamunbd.blogspot.com/2014/03/how-to-create-dynamic-dropdownlist-step.html
    – Md. Ilyas Hasan Mamun Nov 05 '14 at 08:45

2 Answers2

10

Important to note is that if you use DropDownListFor(x => x.Human), the returned value of the dropdownlist should be a Human object.

It isn't. In your own code snippet, you set the value of the SelectListItem to the ID of the Human. Therefore, when you submit your form, you will receive the ID that you selected.

Add the following to your model:

public int HumanId { get; set; }

Bind your dropdownlist to that int:

@Html.DropDownListFor(model => model.HumanId, (SelectList)ViewBag.HumanSelection);

Now, when you get back to the controller, use that ID to look up the actual Human you want:

[HttpPost]
public ActionResult Create (CreateModel model)
{
    if(model.HumanId > 0)
    {
        model.Human = GetHumanByID(model.HumanId);
        //or however you want to get the Human entoty from your database
    }
}

It's a simplified solution, but I suspect your main confusion stems from the fact that you're expecting to receive a Human from the DropDownList, while it will actually only return an int (the ID).

Edit

I don't have much information on your data model, but if you're using entity framework, odds are that your Dog class will have a foreign key property called HumanId. If that is the case, you don't even need to get the Human entity like I showed you before. If you put the selected ID in the HumanId property, Entity Framework should be able to use that to create the relation between Human/Dog you want.

If this is the case, it would seems best to elaborate on this in your question, as this would otherwise be more guesswork than actual confirmation.

Edit 2 going offtopic here

Your code:

db.Humen

The plural form of man is men, woman is women; but for human, it's humans :) Humen does sounds like an awesome suggestion though ;)

SteveCav
  • 6,649
  • 1
  • 50
  • 52
Flater
  • 12,908
  • 4
  • 39
  • 62
  • 1. The error says it doesn't have such a method. 2. The corresponding column in my db is called Human_ID instead of HumanID(like every tutorial I have come across). VS will also not let me autocomplete model.Human_ID. The only valid key is model.Human. 3. The table name was auto generated by the framework. – Prajjwal May 05 '14 at 14:32
  • 1. Please elaborate. Where is the exception thrown? What action is taken? Best to work out in your question,; so everyone is aware of it. 2 It's not the DB column that's important. If you're using entity framework, your code operates relatively separated from the database. It's more to do with how your entity classes are set up. 3. Similar answer, specific database content shouldn't be relevant in cases on Entity Framework. 4. About not letting you complete HumanId; **you need to add that property yourself**. Only then can you use it. – Flater May 05 '14 at 14:35
  • Also, doesn't manipulating the Human_ID col directly defeat the whole point of using EF? I come from rails, and I assumed it was going to handle these things automatically. It auto created tables and the Human_ID column just fine. Is there a helper method that will similarly let me work directly with objects? – Prajjwal May 05 '14 at 14:35
  • I didn't intend to tell you to modify database columns. The point is that if you are using EF, you will most likely have properties in your entity classes that mimic your database's foreign key columns. And if you fill in the HumanId **in your entity** (not the database), EF should pick that up, and make sure the correct data gets added *in the database*. Generally speaking, entity Framework will handle the database for you. All you have to do is make sure the entities (i.e. classes) are set up corectly and contain the correct data. – Flater May 05 '14 at 14:37
  • Yes! I guess what I'm looking for is a helper method that will let me work directly with instances of the Human model? So I don't have to worry about ID's while generating the dropdown? – Prajjwal May 05 '14 at 14:38
  • No, using the ID as value in the dropdownlist is exactly what you want. But you have to bind it to an int (not to a variable of type Human), as it is a number, not a Human. At a later stage, if needed, you can look up the actual Human by using that int from the dropdownlist. – Flater May 05 '14 at 14:40
  • 1
    I am onboard with making humen the plural for human. Again offtopic. – DeadlyChambers Mar 09 '15 at 15:00
  • Edited your post. You needed to cast the ViewBag.HumanSelection to a SelectList, it was causing the problems people mentioned in the comments above. – SteveCav Jul 22 '15 at 06:18
  • "Human" is from Latin. It has no relation to "man" whatsoever. So there's no reason the plural should be "humen". – siride Dec 11 '15 at 15:59
8

The problem is that you are attempting to bind a Human type to a dropdown in the UI, a dropdown whose values are strings (the IDs of Human instances) and text are also strings (the names of Human instances).

What you should be binding to the dropdown instead is the ID of the Human, to match the fact that the ID is being used as the value. So with a view model such as

public class CreateDogModel 
{
    public string Name { get; set; }

    [Range(0, int.MaxValue)]
    public int Human { get; set; }

    public IEnumerable<Human> Humans { get; set; }
}

And the GET controller action

[HttpGet]
public ActionResult Create()
{
    var model = new CreateDogModel 
    {
         Humans = db.Human.ToList()
    };

    return View(model);
}

The view then becomes

@Html.DropDownListFor(
    model => model.Human, 
    Model.Humans.Select(h => new SelectListItem 
        { 
            Text = h.Name, 
            Value = h.ID.ToString() 
        }),
    "Please select a Human");

In your POST controller action, you now look up the chosen human by the Human property value from the View model

[HttpPost]
public ActionResult Create(CreateDogModel model)
{
    if (!ModelState.IsValid)
    {
        // fetch the humans again to populate the dropdown
        model.Humans =  db.Human.ToList();
        return View(model);
    }

    // create and persist a new dog

    return RedirectToAction("Index");
}
Russ Cam
  • 124,184
  • 33
  • 204
  • 266