I am passing an anonymous type into a dynamic partial view as part of the @model
, and one of the properties is a string that contains some HTML. When I use the HtmlHelper
methods to render the property, the Razor engine is encoding the string, resulting in literal text on the page - <i>text</i>
in this case, instead of the desired text
.
Since it is a dynamically typed View, I cannot call the property directly. Specifically, if I try to bind to @Model.MyField
, I get a RuntimeBindingException
:
'object' does not contain a definition for 'MyField'
Ideally I could create a type (or at least an interface) to specify for the view (which I recommend as the optimal solution), but my scope of work does not permit this. Plus I'm using the Partial View
in the first place so that I can recycle the template for different types, which have the same property names but not the same type for those properties (yay legacy code!).
I have looked at several related questions that address similar issues, but the answers do not work for my specific situation (due to needing the anonymous type passed to my @model dynamic
view)).
Displaying HTML String from Model in MVC Razor View
- Lists several failed approaches, settles on creating an
IHtmlString
via@(new HtmlString(stringWithMarkup))
orMvcHtmlString.Create(stringWithMarkup)
- Both of which require a known type or local variable, and don't work for binding a property of an anonymous
object
- Lists several failed approaches, settles on creating an
ASP.NET MVC4 - display HTML containing string as raw HTML
- Accepted answer helps explains what's happening:
All the output from helpers and other elements in Razor are put through HttpUtility.HtmlEncode, unless they implement
IHtmlString
.
- Accepted answer helps explains what's happening:
Attempted solutions
Okay, so I assume I'll just swap my
String
properties out for one of thoseIHtmlString
properties... nope. Since I have anonymous type, the Razor engine doesn't know thatMyField
is anIHtmlString
, and (I assume) calls.ToString()
, which is then encoded as usual.Alright, maybe
@Html.DisplayFor
is smarter? Yes, but access is denied:'System.Web.Mvc.HtmlHelper' has no applicable method named 'DisplayFor' but appears to have an extension method by that name. Extension methods cannot be dynamically dispatched. Consider casting the dynamic arguments or calling the extension method without the extension method syntax.
Oh, right. Dynamically dispatched - I can't call extension methods on anonymous methods, because Razor doesn't know what they are. Because I'm using
@model dynamic
to explicitly tell it, Jedi-style, "You don't need to see its identification". If I always knew what type it was, then yes, I could cast the object or call the extension method without using the syntax - but again,dynamic
andanonymous
. Sort of a chicken and egg issue here.
I found / compiled two solutions, neither of which am I really happy with. YMMV:
Set
ViewBag.MyField
in the parentView
before rendering eachPartial View
.Okay, I should have figured this one out a lot sooner, but had to get reminded of this possibility here because I rarely use it (preferring strongly-typed views whenever possible). I actually did try this early on, but due to the way I'm rendering the Partial multiple times it didn't seem to be appropriate. I actually still don't like it because in the parent view, I have to keep updating
ViewBag.MyField
before every call to@Html.Partial
(6 times for my use case). This puts C# code and variable reuse way down the page in the middle of my content, where it is easy to miss and hard to maintain.Use reflection:
object myField = ((Type)Model.GetType()).GetProperty("MyField").GetValue(Model);
This is ultimately what I decided to go with for my use case. Even though reflection wasn't intended for this scenario, even though it requires a little extra error checking. The people who will be maintaining this are more familiar with reflection than .NET MVC, and it consolidates the code into one spot - on the page that it matters to, and at the top with the rest of the "server-side" manipulations. No repeated calls or hunting down references.
I'm actually not entirely clear on why this works (also works with a
dynamic
instead ofobject
), but I'm assuming it's something to do with the Razor engine is inspecting themyField
object type directly for special rendering of a known type (IHtmlString
), rather than seeing an unknownobject
and needing to access a property that is not known to exist at compile time.
来源:https://stackoverflow.com/questions/31306367/emitting-an-html-string-from-anonymous-type-property-in-a-repeated-dynamically