I am working with MVC3 and using a custom ModelBinder to pass around the ProducerListViewModel that I built.
Here is the controller code I am currently working with:
Function Filter(user As UserModel, viewModel As ProducerListViewModel) As ActionResult
If IsNothing(viewModel) Then
viewModel = New ProducerListViewModel(user)
End If
Return View(viewModel)
End Function
<HttpPost()> _
Function Filter(user As UserModel, viewModel As ProducerListViewModel, <Bind(Prefix:="Filter")> filterModel As ProducerFilterModel) As ActionResult
'update the filter in the view model and send off to the list method
viewModel.Filter = filterModel
Return RedirectToAction("List")
End Function
Function List(user As UserModel, viewModel As ProducerListViewModel) As ActionResult
Return Nothing
End Function
This is the code for the model that is bound in the custom ModelBinder
<Serializable()> _
<ModelBinder(GetType(ProducerListViewBinder))> _
Public Class ProducerListViewModel
<XmlIgnore()> _
Public Property Producers As IEnumerable(Of ProducerModel)
Public Property PagingInfo As New PagingInfoModel("Load More Producers")
Public Property Filter As New ProducerFilterModel()
Public Sub New(user As UserModel)
Me.Filter = ProducerFilterBL.Retrieve(user)
End Sub
End Class
Here is the code for the Binder:
Public Class ProducerListViewBinder
Implements IModelBinder
Private Const __sessionKey As String = "ProducerListView"
Public Function BindModel(controllerContext As ControllerContext, bindingContext As ModelBindingContext) As Object Implements IModelBinder.BindModel
Dim filter As ProducerListViewModel
'check to see if the filter exists in session
If IsNothing(controllerContext.HttpContext.Session(ProducerListViewBinder.__sessionKey)) Then
'load existing filter for user and store for later retrieval
Dim user As UserModel = UserBL.RetrieveUser()
filter = New ProducerListViewModel(user)
ProducerListViewBinder.SetItem(filter)
Else
filter = CType(controllerContext.HttpContext.Session(ProducerListViewBinder.__sessionKey), ProducerListViewModel)
End If
Return filter
End Function
End Class
The flow goes like this:
- User navigates to filter page, hitting the first action (Filter)
- User makes changes to the filter and submits the page, hitting the second action (Filter w/ Post)
- The filter w/ Post action receives the updated filter using model binding (third parameter
filterModel As ProducerFilterModel), updates theProducerListFilterModel, and redirects to the List action
Everything works just fine, but here is my question:
Why does the List action have the updated version of the ProducerFilterModel inside of the ProducerListViewModel?
I love that it works so perfectly, I just want to know why it works.
Now that you have shown the code for your model binder everything is very clear. This model binder stores the
ProducerListViewModelinstance into the session which is what allows it to survive the redirect.The first time the POST
Filteraction is hit, there’s nothing in the session, so your custom model binder does some database lookup or something to retrieve the value:and then stores this value into the session. I guess it is the
ProducerListViewBinder.SetItemthat does this job. Unfortunately you haven’t shown the code of it but I am ready to bet 5 bucks that it’s what it do.Then the
Filteraction executes, and at the end it redirects to theListaction which takes aProducerListViewModelas argument. So your custom model binder kicks in again but this time it finds the instance it previously stored into the session and it simply returns it from there.So there’s no magic here. It simply uses the ASP.NET session in order to persist the values between the redirects.