Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create DropDownList for an ASP.NET MVC5 Relation [duplicate]

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.

like image 200
Prajjwal Avatar asked May 05 '14 14:05

Prajjwal


2 Answers

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 ;)

like image 99
Flater Avatar answered Nov 09 '22 23:11

Flater


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");
}
like image 41
Russ Cam Avatar answered Nov 09 '22 23:11

Russ Cam