3

Hi I i try to use a ToExpando solution to use anonymous classes in razor views. I use this solution -> Dynamic Anonymous type in Razor causes RuntimeBinderException

I'll write what i did:

  1. I added a file Extensions.cs where i put following code:

    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;
        }
    }
    
  2. I wrote a query that receive a tuples from database in controller method:

    IEnumerable<dynamic> articles = (from p in db.Articles.Where(p => p.user_id == 2)
            select new
            {
                p.article_id,
                p.title,
                p.date,
                p.category,
                AverageScore = db.Articles_Scores
                    .Where(o => o.user_id == p.user_id && p.article_id == o.article_id)
                    .Average(m => m.score)
            }).AsEnumerable()
              .Select(r => r.ToExpando());
    int ii = 0;
    foreach(var it in articles) {
        // HERE I CAN READ EVERYTHING
        ii = it.article_id;
    }
    return View(articles);
    
  3. In view I declare a model:

    @model IEnumerable<dynamic>
    
  4. And I try to get every tuples:

    @foreach (dynamic item in Model) {
    // some code
        @item.article_id // HERE IS EXCEPTION
    }
    

In the foreach line I got an Exception:

RuntimeBinderException: 'System.Dynamic.ExpandoObject' does not contain a definition for 'article_id'

What did I do wrong?

Community
  • 1
  • 1
nosbor
  • 2,826
  • 3
  • 39
  • 63

4 Answers4

5

You need to call .AsEnumerable() or .ToList() first to force ToExpando to run on the client.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • Do you mean I should use ToExpando().AsEnumerable()? If so, I gave an similar exception :/ – nosbor Jul 12 '11 at 16:54
  • 1
    No; you need to call `AsEnumerable` _first_ so that `ToExpando` runs on the client – SLaks Jul 12 '11 at 16:55
  • Ok. But I got an Exception: RuntimeBinderException: 'System.Dynamic.ExpandoObject' does not contain a definition for 'article_id' – nosbor Jul 12 '11 at 19:39
3

Try:

dynamic articles = (from p in db.Articles.Where(p => p.user_id == 2_
                select new
                {
                    p.article_id,
                    p.title,
                    p.date,
                    p.category,
                    AverageScore = db.Articles_Scores
                        .Where(o => o.user_id == p.user_id && p.article_id == o.article_id)
                        .Average(m => m.score)
                }).AsEnumerable()
                  .Select(r => r.ToExpando());

Edit: Make sure you declare dynamic not var

Edit 2: In your for look, you're declaring var again. Change it to:

@foreach (dynamic item in Model) {
    // some code
    @item.article_id // HERE IS EXCEPTION
}
Matthew Abbott
  • 60,571
  • 9
  • 104
  • 129
  • I got an Exception: RuntimeBinderException: 'System.Dynamic.ExpandoObject' does not contain a definition for 'article_id' – nosbor Jul 13 '11 at 18:42
  • Thanks for help Matthew but it still doesnt work. I used IEnumerable and now I used your solution but still there is the same problem. It is strange... because it works perfect in controler method(foreach works great and I can read every properties) but in View(Razor) I got the same exception. I really dont know what i'm doing wrong... – nosbor Jul 14 '11 at 15:21
  • I updated my post. Look at code below linq query. I also checked by comment a place where I got exception. – nosbor Jul 14 '11 at 15:52
  • What worked for me is doing this ... (from ... (select new {}).ToExpando()).ToList(). Basically, making sure the expando was on the actual object created, not after. Doing it the other way (With a new select) will also work, and may actually be cleaner looking. – Andrew Sep 19 '11 at 07:18
  • Edit 1 is very important as the objects are created there. In my case Edit 2 was not required i.e. @foreach (var item in Model) worked perfectly – BiLaL Sep 08 '15 at 09:12
1

Ok, building on the first two answers, I added another extension method that I'm surprised I don't see here:

public static List<ExpandoObject> ToExpandoList<T>(this IEnumerable<T> ie) {
    return ie.Select(o => o.ToExpando()).ToList();            
}

And now in my view I have code like this, and it works just fine using Razor.

var peoples = from f in "tom dick susan roberto".Split(' ') 
              select new { FirstName = f, Age = f.Length };
ViewBag.People = peoples.ToExpandoList();
Andrew
  • 8,322
  • 2
  • 47
  • 70
  • linq.ToExpando().ToList() did not worked for me but your helper of .ToExpandoList() is successful. – BiLaL Sep 08 '15 at 09:10
0

The problem is HtmlHelper.AnonymousObjectToHtmlAttributes(), it's replacing '_' with '-' for the property name. Look at the comment for the method.

Sperling
  • 171
  • 5