tags:

views:

139

answers:

3

I have the following View Data:

public class ShoppingCartViewData
{
    public IList<IShoppingCartItem> Cart
    {
        get;
        set;
    }
}

I populate the viewdata in my controller:

viewData.Cart = CurrentSession.CartItems;

return View(viewData);

And send the data to the view and display it using:

<% for (int i = 0; i < Model.Cart.Count; i++ ) { %>
 <%= Html.TextBoxFor(m => m.Cart[i].Quantity)%>
    <%= Html.HiddenFor(m => m.Cart[i].Id) %>
<% } %>

I want to be able to catch the viewdata on the post. When I try:

[HttpPost]
public ActionResult UpdateCart(ShoppingCartViewData viewData)
{
    ...
}

When I run this I get a: System.MissingMethodException: Cannot create an instance of an interface.

Can anyone shed some light on this. What would I have to do to get this to work?

Many Thanks

+1  A: 

You could try adding the formcollection as a parameter. And shouldn't viewdata be the viewmodel you're using?

[HttpPost]
public ActionResult UpdateCart(ShoppingCartViewModel viewModel, FormCollection collection)
{
    ...
}

Not sure if this is the exact solution, i'm also busy learning MVC2.0 and .NET4 ;-)

Rob
I don't think this will solve the problem. It's becuase the data binder is trying to instantiate ShoppingCartViewData which contains an inteface without a concrete implementation.
UpTheCreek
Is there a way around it? Do I have to inject the object into ShoppingCartViewData?
Thomas
Why are you using an interface here anyway?
UpTheCreek
The whole domain model is based on Interfaces. The data coming from Linq2Sql is cast to an Interface as dictated by the domain model. The model is in its own Core project and everything references it.
Thomas
We have a problem of terminology here. You might want to use the terms ViewData and View Model. In your case, you need a View Model classes. ViewData is just something ASP.NET MVC offers to you where you can save some marginal data that can't go anywhere else. But for your case you need strongly typed views with strongly typed controller actions. Then you should be fine.
mare
BTW I had huge problems when trying to use a model that was in a separate projects. Eventually I ended up with moving everything inside a single ASP.NET MVC project, specifically if we are talking about model you should move those classes to the Model or Models folder.
mare
A: 

I'd create a model binder for your ViewModel, and then you can instantiate a concrete type that implements the appropriate interface when it binds to the method parameters.

You can insert logic into your model binder to read the form fields as appropriate and then instantiate the right IList or IShoppingCartItem data, so no need to worry about being pinned to a single implementation of the interface either.

Tejs
Any examples? I am looking for a way for the custom binder to pass a IList<ShoppingCartItem> instead of the IList<IShoppingCart> and populate the values for it. Can't find any examples of that anywhere.
Thomas
This looks like a good resource: http://dotnet.dzone.com/news/custom-aspnet-mvc-model-binder - basically, you just register your interface type with a custom model binder, and when you are returning the object to use, just make sure it implements the right type to pass into your method.
Tejs
A: 

Given my two comments this is how I would do it:

// you don't need this 
// viewData.Cart = CurrentSession.CartItems; 

// return View(viewData); 

// do it like this
return View(CurrentSession.CartItems);

Then have a strongly typed View either this:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Administration.Master" Inherits="System.Web.Mvc.ViewPage<ShoppingCartViewData>" %>

or this:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Administration.Master" Inherits="System.Web.Mvc.ViewPage<List<IShoppingCartItem>>" %>

Also this code won't work. This will generate you a bunch of textboxes with the same name and id. You need to generate textboxes with a count and for that you won't be able to use Html.TextBoxFor(). You will have to revert to Html.TextBox() or create a new extension TextBoxFor() method which would also accept a number (for you count).

<% for (int i = 0; i < Model.Cart.Count; i++ ) { %> 
 <%= Html.TextBoxFor(m => m.Cart[i].Quantity)%> // this won't work, if you want to post back all textboxes after they are edited
    <%= Html.HiddenFor(m => m.Cart[i].Id) %> 
<% } %> 

HTH

mare
But really, please rename the class ShoppingCartViewData to ShoppingCartViewModel.
mare