Почему экземпляр HtmlHelper null в декларативном методе @helper Razor?

Использование RTM MVC 3 Я получаю странный NullReferenceException:

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

Оказывается, что Html (типа HtmlHelper) null.

Я никогда не видел этого раньше в обычном режиме. Я начинаю экспериментировать с декларативными вспомогательными методами в Razor (пока они кажутся немного ограниченными), и я совершенно озадачен тем, что вижу здесь.

Ответ 1

Это известное ограничение этих помощников. Одна из возможностей - передать его в качестве параметра:

@helper TestHelperMethod(HtmlHelper html) {
    var extra = "class=\"foo\"";
    <[email protected](extra)></div>
}

Другая возможность заключается в том, чтобы написать помощник как метод расширения:

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

а затем:

@Html.TestHelperMethod()

Ответ 2

Используя предложение Drew Noakes, я пришел к обходу, который сейчас делает трюк, и который можно легко удалить, как только проблема будет решена в более новой версии MVC (то есть, если больше вещей не изменилось, что бы сломать его:))

Цель состоит в том, чтобы использовать HtmlHelper внутри декларативного вспомогательного метода, который живет в файле в App_Code без исключения NullReferenceException. Чтобы решить эту проблему, я включил во все файлы в App_Code следующее:

@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; }
    }
}

Это похоже на трюк, поскольку теперь я могу написать следующий помощник (в том же файле):

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

Очевидно, что вспомогательный метод является лишь примером. Это решение имеет недостаток, что объявления @functions должны быть введены во всех файлах декларации помощника в App_Code, но не позволяют усложнять вызов помощнику, поскольку вы можете просто написать в виде:

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

Надеюсь, что это поможет...

Ответ 3

Я знаю, что это не так, но если это просто Html.Raw(value), вы надеялись использовать, когда находите этот вопрос в Google (как я был) в соответствии с исходным кодом System.Web.Mvc.dll все Html.Raw do:

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

Итак, я только что использовал @(new HtmlString(value)) в моем помощнике, который прекрасно работает.

Ответ 4

Просто замените

 @Html.Raw(extra)

с

@(new HtmlString(extra))

Ответ 5

Я думаю, что знаю, что вызывает проблему...

Определение атрибута свойства Html:

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

Установка точки останова в моем вспомогательном методе показывает, что CurrentPage на самом деле не является экземпляром WebPage, поэтому значение null.

Ниже приведена иерархия типов CurrentPage (мои имена классов немного модифицированы):

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

Обратите внимание, что базовый класс моего представления указан в Web.config:

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

который определяется как в общей, так и в не общей форме:

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

Итак, если эта проблема не возникает для других, возможно, они не используют пользовательский pageBaseType.

Заметьте, что я разместил объявление @helper в App_Code\Helpers.cshtml в надежде сделать его доступным по всему миру.

Я делаю что-то неправильно или это ошибка?

EDIT Спасибо Дарин за то, что он указал на это как на известную проблему. Тем не менее, почему свойство Html не переопределяется как:

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

У меня была такая же проблема, и эта строка кода сделала трюк. Это не решение для использования HtmlHelper, это просто способ записи RAW html в декларативном помощнике бритвы.

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