Model dynamiczny MVC Razor, „obiekt” nie zawiera definicji „PropertyName”

106

Używanie MVC 3 z silnikiem widoku Razor. Mam ten widok:

@model dynamic
@{
    var products = (List<ListItemBaseModel>)Model.Products;
    var threshold = (int)(Model.Threshold ?? 1);
    var id = Guid.NewGuid().ToString();
}

Jest wywoływana z innego widoku przy użyciu tego kodu:

@Html.Partial("PartialViewName", new { Products = Model, Threshold = 5 })

W obu widokach, kiedy debuguję je i oglądam Model, wydaje się, że zawiera on poprawny obiekt. Kiedy wykonuję kod, w wierszu „var products =” pojawia się błąd o treści:

„obiekt” nie zawiera definicji „produktów”

Czy ktoś może mi wyjaśnić, dlaczego otrzymuję ten błąd? Ponownie, kiedy oglądam obiekt Model w trybie debugowania, wygląda dobrze (ma 2 właściwości: produkty i próg)

Ruud van Falier
źródło

Odpowiedzi:

150

Czy jako model widoku przekazujesz wystąpienie klasy anonimowej? Właśnie wypróbowałem to (model widoku dynamicznego w CSHTML) i otrzymałem ten sam błąd, co twój, gdy używam klasy anonimowej, ale działało dobrze, gdy utworzyłem nazwaną klasę. Szukałem, ale nigdzie nie widziałem tego udokumentowanego.

// error
return View(new { Foo = 1, Bar = "test" });

// worked
return View(new TestClass { Foo = 1, Bar = "test" });

EDYCJA # 1:

Według Davida Ebbo nie można przekazać typu anonimowego do widoku z typami dynamicznymi, ponieważ typy anonimowe są kompilowane jako internal. Ponieważ widok CSHTML jest kompilowany do oddzielnego zestawu, nie może uzyskać dostępu do właściwości typu anonimowego.

EDYCJA # 2:

David Ebbo zredagował swój post z tym wyjaśnieniem:

Uwaga (22.12.2011): teraz, gdy MVC 3 ma bezpośrednie wsparcie dla dynamiki, poniższa technika nie jest już potrzebna. To właśnie ten post doprowadził do integracji tej funkcji z MVC!

Lucas
źródło
1
Zmiana jest miło wiedzieć. Po prostu miałem ten sam problem i nie rozumiałem tam WTF. Dziękuję za wyjaśnienie.
Yanick Rochon
18
EDYCJA # 2 sugeruje, że teraz (MVC> 3) jest możliwe zrobienie linii oznaczonej jako "błąd"? return View(new { Foo = 1, Bar = "test" });? Ponieważ używam MVC 4 i nadal otrzymuję komunikat „obiekt nie zawiera definicji dla Foo”
sport,
@sports me too ... czy znalazłeś obejście? (obok ToExpandojednego)
Alex
2
Tak więc teraz, w 2018 roku, używając widoków ASP.NET Core 2.1 i Razor, stwierdzam, że błąd w oryginalnym pytaniu nadal mnie gryzie. Więc nie wiem, o co chodzi w tej rozmowie o naprawieniu MVC 3, ponieważ nadal wydaje się zepsuty.
Andrew Arnott,
41

Na .NET 4.0 typy anonimowe można łatwo przekonwertować na ExpandoObjects, a zatem wszystkie problemy są naprawiane z narzutem samej konwersji. Sprawdź tutaj

Adaptabi
źródło
Zapraszamy. Może to wyrzuca M $, aby anonimowe typy były bardziej użyteczne
Adaptabi
Czy dotyczy to jednak częściowych? Pojawił się błąd, że Partials nie mogą być dynamicznie wysyłane ...
John Bubriski
1
Jakie częściowe? Czy możesz podać przykład?
Adaptabi
27

Nie ma to nic wspólnego z typami anonimowymi, które mają właściwości wewnętrzne

Jest całkowicie możliwe przekazanie anonimowych typów z widoku do częściowego widoku

Napotkałem dziś ten sam problem i nie miał on (bezpośrednio) nic wspólnego z problemem przekazywania anonimowych typów i ich nieodłącznych internalwłaściwości.

W związku z tym, w odniesieniu do pytania PO, odpowiedź @Lucas jest nieistotna - mimo że obejście zadziała .

W pytaniu PO anonimowy typ jest przekazywany z widoku w zestawie X do częściowego w zestawie X , dlatego problem, który przedstawił David Ebbo, dotyczący właściwości wewnętrznych dla typów anonimowych, nie ma żadnego znaczenia; typy skompilowane dla widoku, typ częściowy i anonimowy są zawarte w tym samym zestawie .

Więc co powoduje nagłe niepowodzenie w przekazaniu anonimowego typu z widoku do częściowego?

Przynajmniej w mojej sytuacji odkryłem, że było to spowodowane innym widokiem w TYM SAMYM FOLDERZE, który określa typ modelu, którego nie można rozwiązać . Widoki są kompilowane w czasie wykonywania, więc miałoby to sens, ponieważ kompilacja widoków w czasie wykonywania oznaczałaby również niepowodzenie kompilacji typów dynamicznych, a częściowa po prostu otrzymałaby plik object. Nie jest od razu oczywiste, co się dzieje, ale w konkretnym przykładzie PO (i moim) jest to najprawdopodobniej przyczyną problemu.

Warto zauważyć, że jeśli typ modelu jest poprawny, ale inna część widoku nie kompiluje się, to nie wpływa to w ten sam sposób na typy anonimowe. Musi to wynikać z tego, jak Razor rozbija dynamiczną kompilację części składowych widoku.

Po poprawieniu nieprawidłowego widoku odbuduj całe rozwiązanie lub wyczyść i odbuduj projekt przed sprawdzeniem, czy został naprawiony.

Aby upewnić się, że nie zostaniesz ponownie zaskoczony, możesz włączyć kompilację w czasie kompilacji widoków Razor, dodając to do swojego csprojpliku:

<PropertyGroup>
    <MvcBuildViews>true</MvcBuildViews>
</PropertyGroup>
joshcomley
źródło
2
To rozwiązało mój problem - użycie „@model dynamic” początkowo wydawało się dobrym rozwiązaniem, ale w rzeczywistości prowadziło mnie na złą ścieżkę.
crimbo
Wyczyściłem rozwiązanie, odbudowałem i błąd zniknął .. 121 głosów nie trafiło.
maxbeaudoin
Zaktualizowałem moją odpowiedź, aby odzwierciedlić obsługę MVC dla modeli widoku dynamicznego od czasu MVC 3.
Lucas
Włączanie kompilacji widoków od czasu do czasu jest zawsze przydatne w przypadku dużej bazy kodu. Ujawnia wszelkiego rodzaju problemy, literówki, błędy z T4MVC dzięki wprowadzonemu silnemu pisaniu itp.
Denis The Menace
Och, racja: właśnie zauważyłem, że mówimy tutaj o przejściu od widoku do częściowego. Nie od kontrolera do widoku, co jest moim problemem.
mwardm
9

Dodaj następującą klasę w dowolnym miejscu w swoim rozwiązaniu (użyj przestrzeni nazw System, aby była gotowa do użycia bez konieczności dodawania żadnych odwołań) -

    namespace System
    {
        public static class ExpandoHelper
        {
            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;
            }

        }
    }

Kiedy wysyłasz model do widoku, przekonwertuj go na Expando:

    return View(new {x=4, y=6}.ToExpando());
Segev -CJ- Shmueli
źródło
1
Wygląda jak unnecssary napowietrznych do mnie, aby utworzyć obiekt dynamiczny, a potem utworzyć ExpandoObject ... Wystarczy stworzyć ExpandoObject zamiast ..
Baz1nga
@ Baz1nga Nie możesz zrobić ... new Expando () {prop = value, ...}, co sprawia, że ​​jest to problematyczne. Używam JObject Json.Net do podobnego użytku.
Tracker1,
3
Źle jest mieć tam HtmlHelpera ... public static ExpandoObject ToExpando (ten obiekt o) {IDictionary <string, object> expando = new ExpandoObject (); foreach (var propertyInfo in o.GetType (). GetProperties ()) {expando.Add (new KeyValuePair <string, object> (propertyInfo.Name, propertyInfo.GetValue (o, index: null))); } return (ExpandoObject) expando; }
erlando
6

Zamiast używać dynamictypu Model w widoku częściowym.

Możesz wywołać anonimowe atrybuty obiektu za pomocą @ViewData.Eval("foo")zamiast @Model.foo.

Następnie możesz usunąć @Model dynamicz widoku.

Niedawno natknąłem się na ten problem podczas przekazywania niektórych atrybutów między widokami w celu integracji komentarzy społecznościowych na Facebooku. Przykładowy kod:

Html.RenderPartial(@"Layouts/Partials/_Comments", new {currentUrl = Model.CurrentPage.GetAbsoluteUrl(), commentCount = 5 });

Wtedy moim zdaniem po prostu miałem ten div:

<div class="fb-comments" data-href="@ViewData.Eval("currentUrl")" data-numposts="@ViewData.Eval("commentCount")" data-width="100%"></div>
JamesG
źródło
0

nie jestem pewien, czy otrzymujesz ten błąd, ponieważ nie wdrażasz obejścia. mam ten sam błąd w częściowym widoku. rozwiązaniem było po prostu wyczyszczenie kompilacji i odbudowanie jej. jeśli składnia jest poprawna, kod powinien działać, ale silnik maszynki do golenia może nie aktualizować poprawnie kodu.

goran
źródło
0

Obejrzałem ten problem za pomocą słownika.

 @Html.Partial("_Partial", new Dictionary<string, string> { { "Key1", "Val1" }, { "Key2", "Val2" }, { "Key3", "Val3" } });
Gerade Geldenhuys
źródło
-6

Aby użyć dynamictypu, musisz odwołać się do Microsoft.CSharpzestawu

the_joric
źródło