.NET MVC and strongly/dynamically typed models

by 30. July 2010 15:59

Soooo.. looks like you're getting the ol'

CS1579: foreach statement cannot operate on variables of type 'object' because 'object' does not contain a public definition for 'GetEnumerator'

Sucks.

Yeah so I ran into this today. It took a while but eventually found that in the Page directive of my .ASPX page I had a strong type and that type had been since deleted.

<%@ Page ... Inherits="System.Web.Mvc.ViewPage<IEnumerable<SomeClassIDeleted>>" %>

Your code may look different. You may, like me, have SomeClassIDeleted in the Inherets line, or you may simply have "System.Web.MVC.ViewPage". Either way, it's going to piss your application off.
Way down below you will get the CS1579 error becuase the Model variable (and item variable) is simply an instance of object and thus doesn't have all the great properties you creatd and are trying to use.

Between me and a colleague who wants to remain anonymous we came up with a few couple of ways to solve this:

  1. The "right" solution:Put the correct type in the IEnumerable type.

    Inherits="System.Web.Mvc.ViewPage<IEnumerable<TheRightType>>"

  2. The kinda crappy way: Leave the Inherets="System.Web.MVC.ViewPage" alone and cast "Model" and implicitly type "item":

    Inherits="System.Web.Mvc.ViewPage"
    ...
    <% foreach (ViewModels.Something item in (List<ViewModels.Something>)Model) {... %>

  3. The (arguably...) awesome way to do it... with a DYNAMIC! (.NET 4.0+ only)
    Inherits="System.Web.Mvc.ViewPage<IEnumerable<dynamic>>"
    ...
    <% foreach (var item in Model) {... %>


    Yeah! I love it. This way if you need to change your controller, or the type it returns, as long as the properties are valid it will work with no other code changes. MY COLLEAGUE didn't seem to think this was quite as awesome as I do, as the "item" variable loses it typing and you lose intellisense and all, but I think that is a small price to pay. He says you may as well use Ruby on Rails. Probably right, but hey.

I'm really loving .NET MVC so far, look for more .NET MVC posts in the near future as I get further into this thang.

Tags: ,

.NET | Development | How To

Comments

???

7/31/2010 1:19:27 AM #

Tonehog


Inherits="System.Web.Mvc.ViewPage<IEnumerable<TheRightType>>"


I think the APress book I read says this is The Right Way® . I think there are so few times when you really would need to bind ViewPage to IEnumerable<dynamic> – it's nice to know that flexibility is there, though.

Tonehog United States | Reply

???

7/31/2010 11:46:31 PM #

Adam Alexander

Your colleague sounds like a really smart guy!  Great find, being able to use dynamic there though.  It is very cool that lets you bypass the static typing so easily if you don't want or need it for your project.

Adam Alexander United States | Reply

???

8/2/2010 12:00:35 AM #

dave thieben

Mike - in my experience, the preferred way is to use a ViewModel, even if there's only one property on it.  yeah, it seems like a waste and generates extra code, but then you get everything you're looking for.

dave thieben United States | Reply

???

8/2/2010 6:52:35 AM #

Jerzakie

@Tonehog: How are you using data on your page if you're not binding any data to it at all?
@Adam: Yes, he's a very smart guy.
@Dave: I am indeed using a ViewModel, consider the "TheRightType" to be a ViewModel. By setting the type in the Page directive to Dynamic, you can change your ViewModel from "TheRightType" to "TheOtherRightType" in the controller without modifying the view at all, as long as "TheOtherRightType" has the necessary properties which are accessed in the view.

Jerzakie United States | Reply

???

8/2/2010 10:20:03 AM #

tonehog

Can you give me a use-case scenario where you'd want to use the dynamic type all the time rather than "TheRightType," then? Losing intellisense as well as strong typing seems masochistic to me. That was my point.

tonehog United States | Reply

???

8/2/2010 5:26:52 PM #

Jerzakie

I don't know about masochistic, but yeah I'm not completely sure where using the dynamic would fit, exactly, but you CAN do it Smile

I would initially say that you could use it if you're using a factory pattern for your viewmodel or something, but in that case you'd probably want to bind the page to and interface.

Jerzakie United States | Reply

???

8/4/2010 12:31:10 AM #

Goya

Hello,

One of the usage might be when you want to retrive data back to view from two tables?

here is my senario and the issue, you might be able to help me. to finish the second part. I try to read dynamic type but I couldn't get anything rather than error.

I have two tables and I’ve created model by using “ADO.NET Entity  Data Model” wizard.


CIMLink_Log table has LogID and Log and PortalServerPathID
CIMLink_ PortalServerPath table has PortalServerPathID and path


My goal is show list of logID,Log, path on the view

On first attempt :

On the controller I have
public ActionResult Index1()
        {
            using (PortalCentralEntities pce = new PortalCentralEntities())
            {
                return View(pce.CIMLink_Log.ToList<CIMLink_Log>());
             }
        }

And on the view  I have directive as


<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<CIMServiceApp.Models.CIMLink_Log>>" %>

And

    <% foreach (var item in  Model) { %>
    

        <tr>
            <td>
                <%:item.CIMLink_ PortalServerPath.Path   %>      “Here I am getting error”
            </td>
        </tr>
    
    <% } %>

Then I am getting following error
“The ObjectContext instance has been disposed and can no longer be used for operations that require a connection”
I know if I define PortalCentralEntities pce = new PortalCentralEntities()in class level I wouldn’t get error but I want to open and close Db in method level not class level.(by use “using” )

Second attempt:


Try to pass data as dynamic type. However I think still should be a way to pass data as strong type.

On the model
public ActionResult Index()
        {
            using (PortalCentralEntities pce = new PortalCentralEntities())
            {
                return View((from log in pce.CIMLink_Log
                             join portalServerPath in pce.CIMLink_PortalServerPath
                             on log.PortalServerPathID equals portalServerPath.PortalServerPathID
                             select new { log.LogID, log.HasIssue, log.IssueDetail, log.LastIndexDate, log.ModifiedDate, portalServerPath.CIMLink_ServerPath.ServerPath }
                    ).ToList ());

            }
        }

On the page
Inherits="System.Web.Mvc.ViewPage<dynamic>"


<% foreach (var item in  Model) { %>
    
        <tr>
            <td>
                <%: item.LogID %>  “How can I cast data here?”

            </td>
            
        </tr>
    
    <% } %>


the error is "'object' does not contain a definition for 'LogID'"


any idea would be appreciated?



Thanks

Goya Canada | Reply

???

8/4/2010 12:53:17 AM #

Jerzakie

This is an excellent case to use, as Dave brought up above, a ViewModel. A ViewModel is a class that encompasses all of the properties of the dataset you want to return.

In your view you will want to pull your dataset (like you're doing with LINQ) and select it into a collection of your ViewModel objects... so you may do something like this:

(basing this off scenario 2)

      List<myViewModel> vm = (from d in [entity or linq to sql or whatever]
                                                            join x in  [some other source]
                                                           on d.whatever equals x.whatever
                                                            select new myViewModel{
                                                                           property1 = d.property1,
                                                                           property2 = x.property2
                                                           }).ToList<myViewModel>();
     return View(vm);

(forgive me if the syntax isn't exactly right, you see where I'm going with this, though)

In your view you can either use a dynamic or IEnumerable<myViewModel> and that should work

Jerzakie United States | Reply

???

8/4/2010 6:30:55 AM #

Goya

I found this
www.gregshackles.com/.../

Goya Canada | Reply

???

8/7/2010 5:27:58 AM #

tonehog

Vagif Abilov has an interesting article on theoretically getting intellisense working for dynamic type entities.

bloggingabout.net/.../...llisense-and-dynamic.aspx

Any more info on how to implement it would be nice.

tonehog United States | Reply

???

11/5/2010 6:12:41 PM #

Tonehog

By the way, with the addition of F# as a first-class language to the CLR, we may be able to finally see built-in type inference from dynamic datatypes, as F#'s parser can do a fine job of inferring the types passed as parameters and return types from a method.

Perhaps in VS11?

Tonehog United States | Reply

???

12/30/2010 3:26:30 PM #

cansionesdecristo

wonderfull, thanks.-

cansionesdecristo Argentina | Reply

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading