views:

418

answers:

3

This code works:

[HttpPost]
public ActionResult Edit(int id, FormCollection fc)
{
    Movie movie =
        (
            from m in _ctx.Movie.Include("MovieActors")
                where m.MovieID == id select m
        ).First();
    MovieActorViewModel movieActor = new MovieActorViewModel(movie);

    if (TryUpdateModel(movieActor))
    {
        _ctx.ApplyPropertyChanges(movieActor.Movie.EntityKey.EntitySetName,
            movieActor.Movie);
        _ctx.SaveChanges();
    }

    return View(movieActor);
}

However, I am not sure how to test this, and in general would much rather have the method take a typed model like:

[HttpPost]
public ActionResult Edit(MovieActorViewModel movieActor)

Is this possible? What changes to my MovieActorViewModel class do I need to make in order to enable this? That class looks like this:

public class MovieActorViewModel
{
    public Movie Movie { get; set; }
    public Actor Actor { get; set; }

    public PublisherDealViewModel(Movie movie)
    {
        this.Movie = movie;
        this.Actor =
            (
                from a in this.Movie.Actors
                    where a.ActorID == 1 select a
            ).First();
    }
}

The view is typed (inherits ViewPage) simple:

<% using (Html.BeginForm()) {%>
    Movie Title: <%= Html.TextBoxFor(model=>model.Movie.Title) %><br/>
    Actor Name: <%= Html.TextBoxFor(model=>model.Actor.Name) %>
<% } %>
A: 

When you are creating a View, check "Create a strongly-typed view", and select MovieActorViewModel as View data class. And in this case you might want "Edit" as your View content type.

The view is strongly typed. This code is in the controller.
Juvaly
+1  A: 

It's absolutely possible. The default binder in MVC2 will attempt to bind the values in the form that is posted to the action based on the names of the fields in the form. If you are using helpers like Html.TextBoxFor() and the names of your Model match up properly, the values in the model should be automagically populated.

If, however, the names in your form don't match up perfectly to the properties on your ViewModel, you may have to do some tweaking. You can use the BindAttribute to customize the way binding happens, to some degree:

public ActionResult SomeAction([Bind(Prefix="MyEntity" Include="Property1,Property2)] MyEntityModel someEntity)

BindAttribute allows you to specify which properties you want bound, and you can use the Exclude parameter to explicitly exclude some from binding. If you want to bind properties from the form to properties of a complex object in your ViewModel (i.e. MyViewModel.MyDataObject) then Prefix can be used. Your form names will be "MyDataObject.FirstName" when you use the strongly-typed helpers in that case. Prefix allows you to specify a prefix for the form field names.

You can also define totally custom Binders. Jimmy Board of LosTechies has a blog post about this.

Dave Swersky
Ok, so if its supposed to happen automagically I'm doing something wrong because the object comes in withboth properties (Movie, Actor) being null even though I'm using the typed html helpers.
Juvaly
View source when running the app and look closely at the form names. They are probably called "Movie.Title" instead of just "Title". Use the Prefix="Movie" of the BindAttribute to fix that.
Dave Swersky
That didn't work.
Juvaly
A: 

Ok, this is weird, and might be related to the specific MVC version installed on my machine... (v2.0.0.0 from the RC1 installation), but I actually solved this by renaming the properties from

public Movie Movie { get; set; }
public Actor Actor { get; set; }

to

public Movie TheMovie { get; set; }
public Actor TheActor { get; set; }

seems like the default binder gets confused when the prop name is the same as the class name?

Juvaly