Why is the HtmlHelper instance null in a Razor dec

2019-01-23 10:38发布

问题:

Using MVC 3 RTM I'm getting a strange NullReferenceException:

@helper TestHelperMethod() {
    var extra = "class=\"foo\"";
    <div @Html.Raw(extra)></div>
}

It turns out that Html (of type HtmlHelper) is null.

I've never seen this before in a regular view. I'm starting to experiment with declarative helper methods in Razor (so far they seem a little limited) and I'm quite stumped by what I'm seeing here.

回答1:

That's a known limitation of those helpers. One possibility is to pass it as parameter:

@helper TestHelperMethod(HtmlHelper html) {
    var extra = "class=\"foo\"";
    <div@html.Raw(extra)></div>
}

Another possibility is to write the helper as an extension method:

public static class HtmlExtensions
{
    public static MvcHtmlString TestHelperMethod(this HtmlHelper)
    {
        var div = new TagBuilder("div");
        div.AddCssClass("foo");
        return MvcHtmlString.Create(div.ToString());
    }
}

and then:

@Html.TestHelperMethod()


回答2:

Using Drew Noakes suggestion, I have come to a workaround that does the trick for now and that can be easily removed once the issue is solved in a newer version of MVC (that is if more stuff isn't changed that would break it:))

The goal is to be able to use an HtmlHelper inside a declarative helper method that lives in a file in App_Code without having a NullReferenceException. To solve this I included in all the files in App_Code the following:

@using System.Web.Mvc;

@functions
{
    private static new HtmlHelper<object> Html
    {
        get { return ((WebViewPage)CurrentPage).Html; }
    }

    private static UrlHelper Url
    {
        get { return ((WebViewPage)CurrentPage).Url; }
    }
}

This does seem to do the trick as I can now write the following helper (in the same file):

@helper PrintAsRaw(string htmlString)
{
     @Html.Raw(htmlString)
}

Obviously the helper method is just an example. This solution has the downside that the @functions declarations has to be introduced in all helper declaration files in App_Code, but does avoid complicating the call to the helper, as you can simply write in a view:

@MyAppCodeFile.PrintAsRaw("<p>My paragraph</p>")

Hope this helps...



回答3:

I know it's not the point but if it is just Html.Raw(value) you were hoping to use when finding this question on Google (as I was) according to the source code of System.Web.Mvc.dll all Html.Raw does is:

public IHtmlString Raw(string calue)
{
    return new HtmlString(value);
}

So I've just used @(new HtmlString(value)) in my helper which works nicely.



回答4:

Just replace

 @Html.Raw(extra)

with

@(new HtmlString(extra))


回答5:

I think I know what's causing the issue...

The definition of the Html property getter is:

public static HtmlHelper Html {
    get { 
        WebPage currentWebPage = CurrentPage as WebPage;
        if (currentWebPage == null) {
            return null;
        } 
        return currentWebPage.Html;
    } 
} 

Setting a breakpoint in my helper method shows that CurrentPage is not in fact an instance of WebPage, hence the null value.

Here is the type hierarchy of CurrentPage (my class names doctored slightly):

ASP._Page_Views_mycontroller_View_cshtml
  My.Site.MyWebViewPage`1
    System.Web.Mvc.WebViewPage`1
      System.Web.Mvc.WebViewPage
        System.Web.WebPages.WebPageBase
          System.Web.WebPages.WebPageRenderingBase
            System.Web.WebPages.WebPageExecutingBase
              System.Object

Note that the base class of my view has been specified in Web.config:

<system.web.webPages.razor>
  <pages pageBaseType="My.Site.MyWebViewPage">
    ...

Which is defined both in generic and non-generic form:

public abstract class MyWebViewPage : WebViewPage { ... }
public abstract class MyWebViewPage<TModel> : WebViewPage<TModel> { ... }

So, if this problem does not occur for others, perhaps they're not using a custom pageBaseType.

Note too that I've placed the @helper declaration in App_Code\Helpers.cshtml in the hope of making it globally accessible.

Am I doing something wrong, or is this a bug?

EDIT Thanks Darin for pointing out this as a known issue. Still, why isn't the Html property redefined as:

public static HtmlHelper Html {
    get { 
        WebPage currentWebPage = CurrentPage as WebPage;
        if (currentWebPage != null) {
            return currentWebPage.Html;
        } 
        WebViewPage currentWebViewPage = CurrentPage as WebViewPage;
        if (currentWebViewPage != null) {
            return currentWebViewPage.Html;
        } 
        return null;
    } 
} 


回答6:

I had the same issue and this line of code did the trick. It´s not a solution for using HtmlHelper, it's just a way of writing RAW html in a declarative razor helper.

@{CurrentPage.WriteLiteral("html code");}