views:

295

answers:

2

Here's a simplification of my real models in ASP.NET MVC, that I think will help focus in on the problem:

Let's say I have these two domain objects:

public class ObjectA
{
    public ObjectB ObjectB;
}

public class ObjectB
{
}

I also have a view that will allow me to create a new ObjectA and that includes selecting one ObjectB from a list of possible ObjectBs.

I have created a new class to decorate ObjectA with this list of possibilities, this is really my view model I guess.

public class ObjectAViewModel
{
    public ObjectA ObjectA { get; private set; }
    public SelectList PossibleSelectionsForObjectB { get; private set; }

    public ObjectAViewModel(ObjectA objectA, IEnumerable<Location> possibleObjectBs)
    {
        ObjectA = objectA;
        PossibleSelectionsForObjectB = new SelectList(possibleObjectBs, ObjectA.ObjectB);
    }
}

Now, what is the best way to construct my view and controller to allow a user to select an ObjectB in the view, and then have the controller save ObjectA with that ObjectB selection (ObjectB already exists and is saved)?

I tried creating a strongly-typed view of type, ObjectAViewModel, and binding a Html.DropDownList to the Model.PossibleSelectionsForObjectB. This is fine, and the I can select the object just fine. But getting it back to the controller is where I am struggling.

Attempted solution 1:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(ObjectAViewModel objectAViewModel)

This problem here is that the objectAViewModel.ObjectA.ObjectB property is null. I was thinking the DropDownList which is bound to this property, would update the model when the user selected this in the view, but it's not for some reason.

Attempted solution 2:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(ObjectA objectA)

This problem here is that the ObjectA.ObjectB property is null. Again, I thought maybe the DropDownList selection would update this.

I have also tried using the UpdateModel method in each of the above solutions, with no luck. Does anyone have any ideas? I'm guessing I'm missing a binding or something somewhere...

Thanks!

A: 

After some more research it doesn't look like this is a case ASP.NET MVC will take care of for me. Perhaps there is a data service binding model I can use (so MVC would automatically grab the appropriate object out of memory, based on what was selected in the dropdown), but for now, I can fix this by handling it in the controller:

  1. Get the selected item from the dropdown using Controller.ModelState
  2. Reload that ObjectB from the underlying data service
  3. Assign that ObjectB to ObjectA.ObjectB
  4. Save ObjectA

So my controller method looks like this now:

Edited based on the comment from LukLed

   [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Create(ObjectA objectA, string objectBStr)
    {
        ObjectB objectB = _objBService.Get(objectBStr);
        objectA.ObjectB = objectB;
        _objAService.Save(objectA);
        return RedirectToAction("Details", new { id = objectA.Id });
    }
manu08
You can change public ActionResult Create(ObjectA objectA) to public ActionResult Create(ObjectA objectA, string ObjectB). You won't have to get values from ModelState.
LukLed
Thanks! I'll do that.
manu08
+1  A: 

I use code as follows:

    [HttpPost]
    public ActionResult Create([Bind(Exclude = "Id")]ObjectA objectAToCreate)
    {
        try
        {
            Repository.AddObjectA(objectAToCreate);
            return RedirectToAction("Details", new { id = objectAToCreate.Id });
        }
        catch
        {
            return View();
        }
    }

With the following code in a Repository (Entity Framework specific):

    public void AddObjectA(ObjectA objectAToAdd)
    {
        objectAToAdd.ObjectB = GetObjectB(objectAToAdd.ObjectB.Id);
        _entities.AddToObjectAs(objectAToAdd);
        _entities.SaveChanges();
    }

    public void GetObjectB(int id)
    {
        return _entities.ObjectBs.FirstOrDefault(m => m.id == id);
    }

As per your commments, it is essentially reloading the object from the underlying data service, however I didn't find the need to use the ModelState to access the attempted value.

This is based on a view coded along these lines:

        <p>
            <%= Html.LabelFor( f => f.ObjectB.Id) %>
            <%= Html.DropDownList("ObjectB.Id", new SelectList((IEnumerable)ViewData["ObjectBList"], "Id", "Descriptor"),"") %>
            <%= Html.ValidationFor( f => f.ObjectB, "*") %>
        </p>

Note that this could be improved to use a strongly typed ViewModel (which I believe you already do) and also to create a custom Editor Template for ObjectB such that the call could be made using:

<%= Html.EditorFor( f => f.ObjectB ) %>
Mark van Proctor