Matt Hamilton
Matt Hamilton

Reputation: 204129

Can I pass an anonymous type to my ASP.NET MVC view?

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?

Upvotes: 47

Views: 49018

Answers (11)

AlexMelw
AlexMelw

Reputation: 2624

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);
    }
}

Upvotes: 1

Raja
Raja

Reputation: 81

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

Thanks

Upvotes: 4

Haacked
Haacked

Reputation: 59001

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. :)

Upvotes: 26

DATEx2
DATEx2

Reputation: 3513

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

Upvotes: 8

Lasse Skindstad Ebert
Lasse Skindstad Ebert

Reputation: 3390

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 
}

Upvotes: 16

Davi F
Davi F

Reputation:

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

Upvotes: 0

Alper
Alper

Reputation: 1403

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.

Upvotes: 1

Matt Hamilton
Matt Hamilton

Reputation: 204129

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.

Upvotes: 4

zadam
zadam

Reputation: 2456

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.

Upvotes: 1

Mitch
Mitch

Reputation: 239

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.

Upvotes: 0

John Boker
John Boker

Reputation: 83699

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.

Upvotes: 0

Related Questions