107

Using MVC 3 with Razor view engine. I have this View:

@model dynamic
@{
    var products = (List<ListItemBaseModel>)Model.Products;
    var threshold = (int)(Model.Threshold ?? 1);
    var id = Guid.NewGuid().ToString();
}

It is called from another view using this code:

@Html.Partial("PartialViewName", new { Products = Model, Threshold = 5 })

In both Views, when I debug them and watch Model, it seems to contain the correct object.
When I execute the code I get an error on the var products = line saying:

'object' does not contain a definition for 'Products'

Why do I see this error?
When I watch the Model object in debugging mode it looks all right (having 2 properties: Products and Threshold)

James Risner
  • 5,451
  • 11
  • 25
  • 47
Ruud van Falier
  • 8,765
  • 5
  • 30
  • 59
  • 2
    possible duplicate of [Dynamic Anonymous type in Razor causes RuntimeBinderException](http://stackoverflow.com/questions/5120317/dynamic-anonymous-type-in-razor-causes-runtimebinderexception) – Rune FS Jun 08 '11 at 09:02

8 Answers8

153

I just tried this (dynamic view model in CSHTML) and got the same error as your when using an anonymous class, but it worked fine if I created a named class. I searched but haven't seen this documented anywhere.

// error
return View(new { Foo = 1, Bar = "test" });

// worked
return View(new TestClass { Foo = 1, Bar = "test" });

David Ebbo clarified that you can't pass an anonymous type into a dynamically-typed view because the anonymous types are compiled as internal. Since the CSHTML view is compiled into a separate assembly, it can't access the anonymous type's properties. Due to this forum post, David Ebbo clarified on (Dec 22 2011) that MVC 3 now has direct support for dynamic.

James Risner
  • 5,451
  • 11
  • 25
  • 47
Lucas
  • 17,277
  • 5
  • 45
  • 40
  • 1
    The edit is nice to know. I just had the same problem and didn't understand the WTF there. Thanks for the explanation. – Yanick Rochon May 17 '13 at 13:50
  • 18
    EDIT #2 suggests that now (MVC > 3) is possible to do line to marked with "error"? `return View(new { Foo = 1, Bar = "test" });` ? Because I'm using MVC 4 and still getting "object does not contain a definition for Foo" – sports Feb 24 '15 at 22:25
  • @sports me too... have you found a workaround? (beside the `ToExpando` one) – Alex from Jitbit Mar 15 '18 at 20:01
  • 2
    So now in 2018 using ASP.NET Core 2.1 and Razor views, I find the error in the original question is still biting me. So I don't know what this talk about MVC 3 fixing this is all about, since it still seems broken. – Andrew Arnott Sep 07 '18 at 18:57
41

On .NET 4.0 Anonymous types can easily be converted to ExpandoObjects and thus all the problems are fixed with the overhead of the conversion itself. Check out here

Community
  • 1
  • 1
DATEx2
  • 3,585
  • 1
  • 24
  • 26
30

This has nothing to do with anonymous types having internal properties

It is perfectly possible to pass anonymous types from a view to a partial view

I encountered the same problem today and it was nothing (directly) to do with the problem of passing anonymous types and their inherent internal properties.

As such, in relation to the OPs question, the answer by @Lucas is irrelevant - even though the workaround will work.

In the OPs question, an anonymous type is being passed from a view in assembly X to a partial in assembly X, therefore the problem that David Ebbo outlined of the properties being internal for anonymous types is of no consequence; the types compiled for the view, the partial and the anonymous type are all contained in the same assembly.

So what is causing the sudden failure to pass an anonymous type from a view to a partial?

At least in my situation, I discovered that it was due to having another view in the SAME FOLDER that specifies a model type that cannot be resolved. Views get compiled at runtime, and so it would make sense as a failure at runtime to compile the views would also mean a failure to compile the dynamic types and the partial would simply receive an object. It's not immediately obvious what is going on, but in the OPs specific example (and mine) this is more than likely the cause of the problem.

It is interesting to note that if the model type is correct but another part of the view doesn't compile then anonymous types are not affected in the same way. This must be down to how Razor breaks up the dynamic compilation of the component parts of the view.

Once you correct the offending view, either rebuild the whole solution or clean and rebuild the project before checking to see if it's fixed.

To ensure you are not caught out by this again you can enable compile time compilation of your Razor views by adding this to your csproj file:

<PropertyGroup>
    <MvcBuildViews>true</MvcBuildViews>
</PropertyGroup>
joshcomley
  • 28,099
  • 24
  • 107
  • 147
  • 2
    This fixed my problem - using "@model dynamic" initially seemed like the right fix, but was in fact taking me down the wrong path. – crimbo Mar 27 '14 at 21:14
  • I cleaned the solution, rebuilt it and the error is gone.. 121 up votes misplaced. – maxbeaudoin Aug 27 '14 at 21:07
  • I've updated my answer to reflect MVC's support for dynamic view models since MVC 3. – Lucas Nov 17 '14 at 17:53
  • Enabling views compilation from time to time is always useful for huge code base. Reveals all sorts of problems, typos, errors with T4MVC thanks to strong-typing it introduced etc. – Denis The Menace Feb 04 '15 at 10:09
  • Oh, right: I've just noticed that we're talking about passing from a view to a partial here. Not from a controller to a view, which is my problem. – mwardm Apr 01 '16 at 12:55
  • How do you determine which view has the compilation error? I added the `MvcBuildViews` property in the csproj file and nothing extra turned up. I physically moved all the other views to an eternal folder. Even when I only had this one view and one partial in the solution, the error still threw. – Jeff Aug 30 '17 at 19:29
  • This solution worked for me. I had the same issue. Fixed after fixing issues in view. – user3404686 Oct 25 '22 at 14:22
10

Add the following class anywhere in your solution (use System namespace, so its ready to use without having to add any references) -

    namespace System
    {
        public static class ExpandoHelper
        {
            public static ExpandoObject ToExpando(this object anonymousObject)
            {
                IDictionary<string, object> anonymousDictionary = HtmlHelper.AnonymousObjectToHtmlAttributes(anonymousObject);
                IDictionary<string, object> expando = new ExpandoObject();
                foreach (var item in anonymousDictionary)
                    expando.Add(item);
                return (ExpandoObject)expando;
            }

        }
    }

When you send the model to the view, convert it to Expando :

    return View(new {x=4, y=6}.ToExpando());
Segev -CJ- Shmueli
  • 1,535
  • 14
  • 15
  • 1
    seems like an unnecssary overhead to me to create a dynamic object first and then create an ExpandoObject... Just create the ExpandoObject instead.. – Baz1nga Jun 19 '12 at 12:26
  • @Baz1nga You can't do ... new Expando() { prop=value, ... }, which makes it problematic. I'm using Json.Net's JObject for similar usage. – Tracker1 Jul 17 '12 at 17:38
  • 3
    It feels wrong to have HtmlHelper in there... public static ExpandoObject ToExpando(this object o) { IDictionary expando = new ExpandoObject(); foreach ( var propertyInfo in o.GetType().GetProperties() ) { expando.Add(new KeyValuePair(propertyInfo.Name, propertyInfo.GetValue(o, index: null))); } return (ExpandoObject) expando; } – erlando Sep 20 '12 at 13:44
9

Instead of using the dynamic Model type within the partial view.

You can call the anonymous object attributes using @ViewData.Eval("foo") instead of @Model.foo.

Then you can remove @Model dynamic from the view.

I came across this issue recently when passing some attributes between views for the Facebook Social Comments Integration. Example code:

Html.RenderPartial(@"Layouts/Partials/_Comments", new {currentUrl = Model.CurrentPage.GetAbsoluteUrl(), commentCount = 5 });

Then in my view I just had this div:

<div class="fb-comments" data-href="@ViewData.Eval("currentUrl")" data-numposts="@ViewData.Eval("commentCount")" data-width="100%"></div>
JamesG
  • 534
  • 4
  • 6
0

i am not sure that you are getting this error because you are not implementing the work-around. i got the same error in a partial view. the solution was just to clean the build and rebuild it. if the syntax is correct, the code should work, but the razor engine may not be updating the code changes properly.

goran
  • 1
0

I worked around this issue by using a Dictionary.

 @Html.Partial("_Partial", new Dictionary<string, string> { { "Key1", "Val1" }, { "Key2", "Val2" }, { "Key3", "Val3" } });
Gerade Geldenhuys
  • 139
  • 1
  • 3
  • 12
-6

To use dynamic type you need to reference Microsoft.CSharp assembly

the_joric
  • 11,986
  • 6
  • 36
  • 57