Dynamic typed ViewPage
Asked Answered
I

4

12

Is this possible? Here's what I'm trying:

    public ActionResult Index()
    {
        dynamic p = new { Name = "Test", Phone = "111-2222" };
        return View(p);
    }

And then my view inherits from System.Web.Mvc.ViewPage<dynamic> and tries to print out Model.Name.

I'm getting an error: '<>f__AnonymousType1.Name' is inaccessible due to its protection level

So basically, is what I'm trying to do just not possible? Why or why not?

Update: here's my view

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

<asp:Content ...>
    <%=Model.Name%>
    <%=Model.Phone%>
</asp:Content>

The View constructor is built-in to the framework.

Instrumentalism answered 24/7, 2009 at 17:21 Comment(2)
Can you post more of your code? I'd like especially to see the constructor to ViewFlavor
Check out my latest update with a link to Phil Haack's blog post.Holierthanthou
F
7

Anonymous types cannot be returned by a method; they are only valid within the scope of the method in which they are defined.

You should use a Model class that you have previously defined and pass that to your View. There is nothing wrong with passing a Model class that does not have every field defined.

Update:

I think I was wrong before. This should work. Perhaps the problem is within the View. Can you post more code? Especially the View and its constructor.

Update the Second:

Ok, I was wrong about passing an anonymous type to another method for use as a dynamic variable -- that can be done.

But I was also wrong in my belief that what you're trying to do would work. Unfortunately for you, it will not. The problem is that you are using ViewPage<TModel>, which uses a ViewDataDictionary<TModel> internally. Because they require strong types, you won't be able to use dynamic objects with them. The internal structure just doesn't use dynamic internally, and specifying dynamic as the type fails.

What would be needed is a DynamicViewPage class and corresponding DynamicViewDataDictionary class that accept object and store it internally as a dynamic. Then you could use an anonymous type and pass it to your Views.

That said, you would not gain anything. You would be able to specify your Views as you have done (i.e. <%=Model.Name%>), but you would not benefit from strong typing. There would be no intellisense and there would be no type safety. You'd do just as well to use the untyped ViewDataDictionary as @Dennis Palmer suggests.

This has been an interesting (and, unfortunately for me, absorbing) thought experiment, but I think, ultimately, that it's not going to happen. Either declare a public type and pass it to your Views, or use the untyped dictionary.

Flavor answered 24/7, 2009 at 17:22 Comment(4)
But I could access members of the object through reflection, which is why I thought dynamic typing might work.Instrumentalism
hmm... and your error suggests that it occurs from within your View, meaning that the passing of the anonymous type to the View works, but that the default protection level of Name is protected or private. And you can't specify the protection level of an anonymous type when you declare it.Flavor
Now that it's been accepted can you edit to only the last update?Penal
@C.Ross: I'm not sure I understand the question.Flavor
H
7

What benefit were you hoping to get from using the dynamic type here?

Using the ViewData dictionary is a very easy way of adding arbitrary objects/items to your view output.

You don't need reflection to get the property names within your View. Just use ViewData.Keys to get the collection of names.

Edit: I've just learned a bit more about dynamics myself and I think maybe you need to create your own dynamic object class that inherits from DynamicObject. You'll want to have a private dictionary in that class and then override TrySetMember and TryGetMember.

Edit Aside: I think one advantage of a strongly typed ViewModel is that you can accept it as a parameter in your POST Action methods. The MVC framework will handle the model binding and in the action method you simply have an instance of your ViewModel class. I don't think you'll have that advantage with a dynamic even if they do work.

Edit Result: Well, I tried using a class derived from DynamicObject, but VS2010 crashes when it tries to render the view. I don't get any exception, just a hard crash and Visual Studio restarts. Here's the code I came up with that causes the crash.

The custom dynamic class:

public class DynViewModel : DynamicObject
{
    private Dictionary<string, object> ViewDataBag;
    public DynViewModel()
    {
        this.ViewDataBag = new Dictionary<string, object>();
    }
    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        this.ViewDataBag[binder.Name] = value;
        return true;
    }
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = this.ViewDataBag[binder.Name];
        return true;
    }
}

In the controller:

public ActionResult DynamicView()
{
    dynamic p = new DynamicViewModel.Models.DynViewModel();
    p.Name = "Test";
    p.Phone = "111-2222";

    return View(p);
}

My view is basically the same as what is listed in the question:

<p>Name: <%=Model.Name %></p>
<p>Phone: <%=Model.Phone %></p>

My Conclusion: This might work, but in the Beta 1 of VS2010 I can't figure out why my code causes Visual Studio to crash. I'll try it again in VS2010 Beta 2 when it is released because it is an interesting exercise in learning about dynamics. However, even if this were to work, I still don't see any advantage over using the ViewData dictionary.

Phil Haack to the rescue! Here's a blog post by Phil Haack that might help you out. It looks like it is what you were looking for. Fun With Method Missing and C# 4

Holierthanthou answered 24/7, 2009 at 17:41 Comment(1)
Well, I'm not really 100% sure yet, I'm just experimenting a bit with 4.0. I guess what I'd like to see is the ability to use the ViewModel pattern without actually having to explicitly write a bunch of ViewModels, but I'm also trying to get a deeper understanding of dynamic typing in C# as well.Instrumentalism
P
1

The actual error here (<>f__AnonymousType1.Name' is inaccessible due to its protection level) is the result of using anonymous types. Anonymous types are implicitly internal (at least in C#), therefore they can only be accessed normally from the same assembly. Since your view is compiled into a separate assembly at runtime, it can't access the internal anonymous type.

The solution is to pass concrete/named classes as models to your view. The view itself can still use dynamic if you want.

Phosphatize answered 31/3, 2011 at 15:35 Comment(0)
S
1

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

Speciosity answered 14/4, 2011 at 23:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.