1

I'm using C# and MVC 5, but for some reason my view is passing an anonymous type back to the controller, and it's not happy about that. The specific error is:

An exception of type 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' occurred in System.Core.dll but was not handled in user code

Additional information: 'object' does not contain a definition for 'Id'

This is the specific controller function signature:

[HttpPost, ParameterBasedOnFormName("save-continue", "continueEditing")]
public ActionResult TaskEdit(dynamic model, bool continueEditing)

The error occurs when I try to reference model.Id in that function. The beginning of the view is this:

@model ProjectModelShopModel
// ...
@using (Html.BeginForm(null, null, FormMethod.Post, Model))
{
    // code
}

How do I solve this error? I can provide more code if necessary.

EDIT: I use a dynamic type in TaskEdit because three views call that function, each with a different model. The function is nearly identical in each. I don't use inheritance because I screwed up too much early on and it would take way too much work to fix now.

vaindil
  • 7,536
  • 21
  • 68
  • 127
  • 2
    why are you using anonymous type? – Ehsan Sajjad Sep 04 '15 at 17:32
  • you should be passed "ProjectModelShopModel" in the TaskEdit action instead of the "dynamic" type – Ahmed ilyas Sep 04 '15 at 17:33
  • Because of the abundant abuse of the word "dynamic" in programming (everything is dynamic), this is hard to research. I **think** the default modelbinder can't really bind to a dynamic model, as it searches for existing properties on the type of the parameter, of which there are none. Try using a `FormCollection collection` as parameter instead to bypass the modelbinder and read the form data yourself. Of course you should strive to create a model that captures the data you want to post, as this makes programming the controller a lot easier. – CodeCaster Sep 04 '15 at 17:35
  • I just clarified why I'm using a `dynamic` type in the controller in an edit. – vaindil Sep 04 '15 at 17:36
  • 2
    FWIW, you should almost never type anything `dynamic`. I say almost never because there's exceptions to every rule, but 99.99% of the time I've seen `dynamic` used by developers it's the wrong way to go or simply a sign of laziness. – Chris Pratt Sep 04 '15 at 17:36
  • @CodeCaster Not sure how to utilize that type. Could you clarify? – vaindil Sep 04 '15 at 17:37
  • @ChrisPratt I know, I should've used inheritance, but I'm still pretty new to C#/MVC and it confused the hell out of me. It'd be too much work to fix all of it now; I unfortunately don't have time. – vaindil Sep 04 '15 at 17:38
  • @CodeCaster So... instead of `model.Id` I would need to use `int.Parse(model["CreatedById"])`? – vaindil Sep 04 '15 at 17:43

2 Answers2

4

This post confirms (without source) my guess that the default modelbinder can't really work with dynamic parameters.

The default modelbinder looks for existing properties on the parameter types (of which dynamic has very little), and then tries to map the posted fields to those properties.

Workarounds are to use public ActionResult TaskEdit(FormCollection formCollection) and fill your model in your controller, or to use a custom modelbinder.

Community
  • 1
  • 1
CodeCaster
  • 147,647
  • 23
  • 218
  • 272
  • As much extra work as this will cause, would the best option ultimately be to use polymorphism correctly? – vaindil Sep 04 '15 at 17:48
  • @Vaindil yes, definitely. – CodeCaster Sep 04 '15 at 17:48
  • 1
    Alright, that makes sense then. `dynamic` has caused me nothing but headaches and I'll have to properly figure out polymorphism at some point, so I'll accept your answer. (Mainly because it does actually answer the question, but also because of your comment.) – vaindil Sep 04 '15 at 17:49
  • Polymorphism will allow you to share bits of code that operate on the base class, perhaps even use editor templates to reduce form rendering and processing duplication. It's most certainly the way to go, thanks for seeing the light. ;) – CodeCaster Sep 04 '15 at 17:50
2

It is model binding problem. You are sending a post request to a single method with 3 types of models. That method has to accept that model and expose you the values of that. There are couple of ways to achieve this -

  1. Create your own model binder

    [HttpPost]
    [ModelBinder(typeof(CustomModelBinder))]
    

CustomModelBinder is an extension of default model binder. You can find the implementation here

  1. Use Form Collection instead of binding to specific model to get data

    [HttpPost]
    public ActionResult ActionName(FormCollection formData) 
    {
        var variablename = Request.Form["VariableName"]; 
    }
    

You can find the example here

Edit Modified example link for formcollection

lazy
  • 531
  • 2
  • 4
  • That FormCollection article on c-sharpcorner is really, really crappy. In the first place because he doesn't even use the FormCollection in the code, but it's also full of errors, lacks any explanation and uses archaic pre-razor syntax. – CodeCaster Sep 04 '15 at 17:52