2

Caveat: This might be an inappropriate use of C#'s dynamic keyword and I probably should be using a strongly-typed view model, but...

I'm trying to avoid creating a strongly-typed view model by passing a C# 4 dynamic type to my view. I have this in my controller:

    public ActionResult Index()
    {
        var query =
            from fr in db.ForecastRates
            join c in db.Codes
            on 
                new { Code = fr.RateCode, CodeType = "ForecastRate" }
            equals 
                new { Code = c.CodeValue, CodeType = c.CodeType }
            select new
            {
                RateCode = fr.RateCode,
                RateCodeName = c.CodeName,
                Year = fr.Year,
                Rate = fr.Rate,
                Comment = fr.Comment
            };

        // Create a list of dynamic objects to form the view model
        // that has prettified rate code
        var forecastRates = new List<dynamic>();

        foreach (var fr in query)
        {
            dynamic f = new ExpandoObject();

            f.RateCode = fr.RateCode;
            f.RateCodeName = fr.RateCodeName;
            f.Year = fr.Year;
            f.Rate = fr.Rate;
            f.Comment = fr.Comment;

            forecastRates.Add(f);
        }

        return View(forecastRates);
    }

...and this in my view (I'm using MVC 3's Razor view engine):

        @inherits System.Web.Mvc.WebViewPage<IEnumerable<dynamic>>

        ...

        <tbody>
            @foreach (var item in Model) {
            <tr>
                <td>@item.RateCodeName</td>
                <td>@item.Year</td>                            
                <td>@item.Rate</td>
                <td>@item.Comment</td>
            </tr>
            }
        </tbody>

I don't like how I iterate through the LINQ result to form the List of dynamic objects.

I'd like to initialize each ExpandoObject inside the LINQ query, but that doesn't seem to be supported.

I tried casting the the query result as List, but that didn't work because you can't convert anonymous type to dynamic.

Derek Morrison
  • 5,456
  • 4
  • 31
  • 24
  • What don't you like about the way you're iterating? And why are you trying to avoid using a strongly-typed view model? Oh, and you definitely *can* convert from an anonymous type to dynamic, so I'm not sure what your last point is trying to say... Are you talking about the lack of variance in `List`? – Jon Skeet Sep 21 '10 at 08:47
  • I just don't want to iterate to keep it a little DRYer (want to do it all in the LINQ query). I want to avoid using a strongly-typed view model and use dynamic instead because I don't want to repeat stuff that's in the model (and because I'm a wannabe Ruby developer doing C# at my job :). Though, maybe I could subclass the model to form the view model? Yes, I want to convert the result of the query to List (is this possible?). Thanks! – Derek Morrison Sep 21 '10 at 10:02
  • Have a look at 'Clay'. See my answer to previous question: http://stackoverflow.com/questions/3758157/asp-net-mvc-having-one-model-for-all-views/3758368#3758368 – Clicktricity Sep 22 '10 at 11:50

2 Answers2

4

Like you said, it's not supported. (I'm not saying dynamic View Models aren't supported - I'm saying what you're trying to do is not)

You could probably neaten up the LINQ query, but in the end your best bet would be to simply create a custom View Model. Seriously, it will take you about 30 seconds to do that.

I know dynamic is new and cool and everything, but your code will be a lot neater and easier to maintain if you just stick with a custom View Model in this case.

I would only go with a dynamic View Model in the very simple scenarios - most of the time you probably want to stick with what we've been doing all along - custom View Models.

Jaco Pretorius
  • 24,380
  • 11
  • 62
  • 94
0

Ok, you could do the following, but I wouldn't recommend it. Create a static method similar to the following

public static IHtmlString DisplayProperty(object obj, string property) {
    return new HtmlString(TypeDescriptor.GetProperties(obj)[property].GetValue(obj).ToString());
}

Then in your cshtml file make the following call (make sure to using your proper namespace)

<tbody>
    @foreach (var item in Model) {
    <tr>
        <td>@DisplayProperty(x, "RateCodeName")</td>
        <td>@DisplayProperty(x, "Year")</td>                            
        <td>@DisplayProperty(x, "Rate")</td>
        <td>>@DisplayProperty(x, "Comment")</td>
    </tr>
    }
</tbody>

I wouldn't recommend this though but it is a solution to your problem that doesn't require a model.

Buildstarted
  • 26,529
  • 10
  • 84
  • 95