21

I want to create a view using razor template, but I do not want to write a class for model, because in many views i will have many queries which will be returning diferent models.

For example I have a linq query:

from p in db.Articles.Where(p => p.user_id == 2)
select new
{
    p.article_id, 
    p.title, 
    p.date, 
    p.category,
    /* Additional parameters which arent in Article model */
};

I need to write a View for this query. This query returns a Articles.

Now I dont know how should looks like a model definition.

I tried to use this deffinition:

@model System.Collections.IEnumerable

But then I had an erros than fileds doesnt exists in object type:

*CS1061: 'object' does not contain a definition for 'addition_field' and no extension method 'addition_field' accepting a first argument of type 'object' could be found*

This is my model for which I do not want to write a next model. Of course

Gabe Moothart
  • 31,211
  • 14
  • 77
  • 99
nosbor
  • 2,826
  • 3
  • 39
  • 63
  • `@model System.Collections.IEnumerable
    `
    – Chris Diver Jul 07 '11 at 15:21
  • That query seems to project an `IEnumerable` of anonymous types, not of `Article` – Russ Cam Jul 07 '11 at 15:24
  • 1
    Can you explain more why you don't want to write a model? There are advantages in using model classes - e.g. you can use intellisense, you can make more use of partial classes and can add attributes for things like validation to the model classes. – Stuart Jul 07 '11 at 15:29
  • This post is nearly a duplicate of http://stackoverflow.com/questions/3758612/simplest-way-to-do-dynamic-view-models-in-asp-net-mvc-3 which has an answer for you: This can not be done. You need to loop over the anonymous enumerable and convert them to dynamic objects – Nicholas Jul 07 '11 at 15:32
  • @Stuart - my personal reason for not using model classes is that in almost all cases they aren't required, especially for view-only pages. Not having them lets me adjust and play with the code very easily, rather than re-writing the model classes, having all the extra files, and what are generally headaches maintaining something that is not needed. Now, if VS.Net (or resharper/refactorpro) made working with them super transparent, I would consider it. The best I have found is deleting the model class, and having R! regenerate it based on the Linq query. – Andrew Sep 19 '11 at 06:39

4 Answers4

46

The short answer is that using anonymous types is not supported, however, there is a workaround, you can use an ExpandoObject

Set your model to @model IEnumerable<dynamic>

Then in the controller

from p in db.Articles.Where(p => p.user_id == 2)
select new
{
    p.article_id, 
    p.title, 
    p.date, 
    p.category,
    /* Additional parameters which arent in Article model */
}.ToExpando();

...
public static class Extensions
{
    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;
    }
}
Community
  • 1
  • 1
Brook
  • 5,949
  • 3
  • 31
  • 45
5

The simplest solution if you are using C# 7.0+ (introduced in Visual Studio 2017+) is to use a tuple rather than an anonymous type.

Razor View: "_MyTupledView.cshtml"

@model (int Id, string Message)

<p>Id: @Model.Id</p>
<p>Id: @Model.Message</p>

Then when you bind this view, you just send a tuple:

var id = 123;
var message = "Tuples are great!";
return View("_MyTupledView", (id, message))
raterus
  • 1,980
  • 20
  • 23
1

It seems you can't pass anonymous types but if you just want the values of the type you might pass an enumerable of an object array to view.

View:

@model IEnumerable<object[]>   

@{
    ViewBag.Title = "Home Page";
}

<div>   
    <table>
        @foreach (var item in Model)
        {
            <tr>
                <td>@item[0].ToString()</td>
                <td>@item[1].ToString()</td>
            </tr>
        }
    </table>
</div>

Controller:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;

    namespace ZZZZZ
    {
        public class HomeController : Controller
        {
            public ActionResult Index()
            {

                List<object[]> list = new List<object[]> { new object[] { "test1", DateTime.Now, -12.3 } };

                return View(list);
            }


        }

    }
MirlvsMaximvs
  • 1,453
  • 1
  • 24
  • 34
1

I think this is an even better solution:

http://buildstarted.com/2010/11/09/razor-without-mvc-part-iii-support-for-nested-anonymous-types/

This allows for nested anonymous types, which the aforementioned expando-object solution won't handle.

Christopher Davies
  • 4,461
  • 2
  • 34
  • 33