问题
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
IHtmlStringvia@(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
Stringproperties out for one of thoseIHtmlStringproperties... nope. Since I have anonymous type, the Razor engine doesn't know thatMyFieldis anIHtmlString, and (I assume) calls.ToString(), which is then encoded as usual.Alright, maybe
@Html.DisplayForis 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 dynamicto 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,dynamicandanonymous. Sort of a chicken and egg issue here.
回答1:
I found / compiled two solutions, neither of which am I really happy with. YMMV:
Set
ViewBag.MyFieldin the parentViewbefore 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.MyFieldbefore 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
dynamicinstead ofobject), but I'm assuming it's something to do with the Razor engine is inspecting themyFieldobject type directly for special rendering of a known type (IHtmlString), rather than seeing an unknownobjectand 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