47

I've just started working with ASP.NET MVC now that it's in beta. In my code, I'm running a simple LINQ to SQL query to get a list of results and passing that to my view. This sort of thing:

var ords = from o in db.Orders
           where o.OrderDate == DateTime.Today
           select o;

return View(ords);

However, in my View, I realised that I'd need to access the customer's name for each order. I started using o.Customer.Name but I'm fairly certain that this is executing a separate query for each order (because of LINQ's lazy loading).

The logical way to cut down the number of queries would be to select the customer name at the same time. Something like:

var ords = from o in db.Orders
           from c in db.Customers
           where o.OrderDate == DateTime.Today
               and o.CustomerID == c.CustomerID
           select new { o.OrderID, /* ... */, c.CustomerName };

return View(ords);

Except now my "ords" variable is an IEnumerable of an anonymous type.

Is it possible to declare an ASP.NET MVC View in such a way that it accepts an IEnumerable as its view data where T is defined by what gets passed from the controller, or will I have to define a concrete type to populate from my query?

Matt Hamilton
  • 200,371
  • 61
  • 386
  • 320

11 Answers11

26

Can you pass it to the view? Yes, but your view won't be strongly typed. But the helpers will work. For example:

public ActionResult Foo() {
  return View(new {Something="Hey, it worked!"});
}

//Using a normal ViewPage

<%= Html.TextBox("Something") %>

That textbox should render "Hey, it worked!" as the value.

So can you define a view where T is defined by what gets passed to it from the controller? Well yes, but not at compile time obviously.

Think about it for a moment. When you declare a model type for a view, it's so you get intellisense for the view. That means the type must be determined at compile time. But the question asks, can we determine the type from something given to it at runtime. Sure, but not with strong typing preserved.

How would you get Intellisense for a type you don't even know yet? The controller could end up passing any type to the view while at runtime. We can't even analyze the code and guess, because action filters could change the object passed to the view for all we know.

I hope that clarifies the answer without obfuscating it more. :)

Haacked
  • 58,045
  • 14
  • 90
  • 114
  • If anyone knows it'd be you, Phil. Thanks for being so accessible to plebs like me! – Matt Hamilton Oct 22 '08 at 00:13
  • Did this answer the question? I have this exact problem and this answer does not help. Could you maybe show how we can loop through ords and display in a view? – KP. Jan 16 '09 at 18:12
  • 11
    It does not answer the question! Surprisingly it works for the textbox, but if I wanted just to render this value, how do I do it? <%= Model.Something %> doesn't work, you'll get 'object' does not contain a definition for 'Something' exception. – PawelRoman Aug 31 '10 at 16:41
  • @Robert maybe because it answered the question. The question is, can you pass it to the view. Yes. Can you do it in such a way that the generic argument type for the view is determined by what's passed to it by the controller? No. I'll update my answer with more details. :) – Haacked Aug 19 '11 at 20:32
  • 1
    There is another solution present in ASP.NET MVC Futures called DynamicViewPage. http://t.co/9RVYMBB – user571646 Aug 20 '11 at 07:48
16

You can pass anonymous types to a view, just remember to cast the model to a dynamic.

You can do like this:

return View(new { 
    MyItem = "Hello", 
    SomethingElse = 42, 
    Third = new MyClass(42, "Yes") })

In the top of the view you can then do this (using razor here)

@{
    string myItem = (dynamic)Model.MyItem;
    int somethingElse = (dynamic)Model.SomethingElse;
    MyClass third = (dynamic)Model.Third;
}

Or you can cast them from the ViewData like this:

@{
    var myItem = ViewData.Eval("MyItem") as string
    var somethingElse = ViewData.Eval("SomethingElse") as int?
    var third = ViewData.Eval("Third") as MyClass 
}
Lasse Skindstad Ebert
  • 3,370
  • 2
  • 29
  • 28
  • 11
    Well, your first example doesn't work. At least not in MVC3. Surprise! First of all, in untyped views, `Model` is already `dynamic`, no need to cast it. And second, with your code, I get a `RuntimeBinderException` with message "*'object' does not contain a definition of MyItem*". Any ideas? – Fyodor Soikin Mar 19 '11 at 19:05
  • 8
    I've just dug up an explanation. Turns out, anonymous types are internal, which means their properties can't be seen outside their defining assembly. Take a look here: http://www.gregshackles.com/2010/09/anonymous-view-models-in-asp-net-mvc-using-dynamics/ – Fyodor Soikin Mar 19 '11 at 19:33
  • in asp.net core .net5: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'object' does not contain a definition for 'myItem' – Lei Yang Jul 01 '21 at 02:36
  • I haven't been using .NET for the past 10 years, so I can't answer why this does not work with .NET5 Core :P – Lasse Skindstad Ebert Jul 04 '21 at 21:55
8

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

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

Here is an article explaining about passing Anonymous type to Views and binding the data.

Thanks

Baby Groot
  • 4,637
  • 39
  • 52
  • 71
Raja
  • 81
  • 2
4

For what it's worth, tonight I discovered the DataLoadOptions class and its LoadWith method. I was able to tell my LINQ to SQL DataContext to always load a Customers row whenever an Orders row is retrieved, so the original query now gets everything I need in one hit.

Matt Hamilton
  • 200,371
  • 61
  • 386
  • 320
  • Wow... Huge thanks -- I've been looking frantically for this for the last several hours. I knew it must exist but wasn't sure where to look. Thanks man! – Andrew Flanagan May 27 '09 at 00:53
1

You can write a class with the same properties of your anonymous type's, and you can cast your anonymous type to your hand-written type. The drawback is you have to update the class when you make projection changes in your linq query.

Alper
  • 1,393
  • 13
  • 16
  • 1
    That then wholly defeats the purpose of using an anonymous type from the action method. At that point, you might as well just return a class instance. – Peter Nov 10 '16 at 16:20
1

This post shows how you can return an anonymous type from a method, but it is not going to suit your requirements.

Another option may be to instead convert the anonymous type into JSON (JavaScriptSerializer will do it) and then return that JSON to the view, you would then need some jQuery etc to do what you like with it.

I have been using Linq to 'shape' my data into a JSON format that my view needs with great success.

zadam
  • 2,416
  • 2
  • 23
  • 32
  • The JSON idea sounds intriguing, but if I'm going to add code to do it then I might as well define a class that has my order and customer properties in it and abandon the anonymous type idea. +1 anyway though. – Matt Hamilton Oct 21 '08 at 23:22
  • Agreed. My primary JSON usage scenario has been returning a JSON object from an ajax call that is used to populate a grid (eg Flexigrid), the grid requires the data in a particular format, Linq rocks at helping me shape the data into that format. – zadam Oct 21 '08 at 23:59
1

Remember: anonymous types are internal, which means their properties can't be seen outside their defining assembly.

You'd better pass dynamic object (instead of anonymous one) to your View by converting anonymous type to dynamic, using an extension method.

public class AwesomeController : Controller
{
    // Other actions omitted...
    public ActionResult SlotCreationSucceeded(string email, string roles)
    {
        return View("SlotCreationSucceeded", new { email, roles }.ToDynamic());
    }
}

The extension method would look like this:

public static class DynamicExtensions
{
    public static dynamic ToDynamic(this object value)
    {
        IDictionary<string, object> expando = new ExpandoObject();

        foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(value.GetType()))
            expando.Add(property.Name, property.GetValue(value));

        return (ExpandoObject) expando;
    }
}

Nevertheless you are still able to pass an anonymous object, but you'll have to convert it to a dynamic one later on.

public class AwesomeController : Controller
{
    // Other actions omitted...
    public ActionResult SlotCreationSucceeded(string email, string roles)
    {
        return View("SlotCreationSucceeded", new { email, roles });
    }
}

View:

@{
    var anonymousModel = DynamicUtil.ToAnonymous(Model, new { email = default(string), roles = default(string) });
}

<h1>@anonymousModel.email</h1>
<h2>@anonymousModel.roles</h2>

The helper method would look like this:

public class DynamicUtil
{
    public static T ToAnonymous<T>(ExpandoObject source, T sample)
        where T : class
    {
        var dict = (IDictionary<string, object>) source;

        var ctor = sample.GetType().GetConstructors().Single();

        var parameters = ctor.GetParameters();

        var parameterValues = parameters.Select(p => dict[p.Name]).ToArray();

        return (T) ctor.Invoke(parameterValues);
    }
}
AlexMelw
  • 2,406
  • 26
  • 35
0

I'm with the same problem... after thinking a bit, I came to the conclusion that the most correct and most scalable solution, its to serialize this anonymous type before sending to the View. So, you can use the same method to fill the page using the View code behind and to populate your page using JSON

0

you may be able to pass an Object and use reflection to get your desired results. Have a look at ObjectDumper.cs (included in csharpexamples.zip) for an example of this.

John Boker
  • 82,559
  • 17
  • 97
  • 130
  • Yeah, I was thinking that using reflection to crack the anonymous type open and treat it as a Dictionary might be the only solution. Bit of a bummer if that's the case. – Matt Hamilton Oct 21 '08 at 22:54
0

If I'm not mistaken, anonymous types are converted into strongly typed objects at compile time. Whether the strongly typed object is valid for view data is another question though.

Mitch
  • 239
  • 2
  • 8