Использование расширений MVC HtmlHelper из декларативных представлений Razor

121

Я пытался создать декларативный помощник Razor в моей папке App_Code для проекта MVC 3 RTM.

Проблема, с которой я столкнулся, заключалась в том, что расширения MVC HtmlHelper, такие как ActionLink, недоступны. Это связано с тем, что скомпилированные помощники являются производными от System.Web.WebPages.HelperPage, и, хотя они предоставляют Htmlсвойство, его тип, System.Web.WebPages.HtmlHelperа не System.Web.Mvc.HtmlHelper.

Пример ошибки, которую я получал:

'System.Web.Mvc.HtmlHelper' не содержит определения для 'ActionLink' и не может быть найден метод расширения 'ActionLink', принимающий первый аргумент типа 'System.Web.Mvc.HtmlHelper' (отсутствует ли у вас директива using или ссылка на сборку?)

Моим единственным решением было создать свой собственный HelperPage и переопределить свойство Html:

using System.Web.WebPages;

public class HelperPage : System.Web.WebPages.HelperPage 
{
    // Workaround - exposes the MVC HtmlHelper instead of the normal helper
    public static new HtmlHelper Html
    {
        get { return ((System.Web.Mvc.WebViewPage) WebPageContext.Current.Page).Html; }
    }
}

Затем я должен написать следующее в верхней части каждого помощника:

@inherits FunnelWeb.Web.App_Code.HelperPage
@using System.Web.Mvc
@using System.Web.Mvc.Html

@helper DoSomething()
{
    @Html.ActionLink("Index", "Home")
}

Это должно быть так сложно в MVC 3, или я что-то делаю не так?

Пол Стовелл
источник
4
Если вам также нужен помощник Url, вы должны добавить эту строку кода в HelperPage: public static UrlHelper Url {get {return new UrlHelper (Html.ViewContext.RequestContext); }}
Марко Стаффоли,

Ответы:

42

Взгляните на Marcindответ на этот вопрос. То, что вы испытываете, - это ограничение размещения декларативных представлений в App_Codeпапке.

Помещение ваших помощников в App_Code работает, но имеет определенные ограничения, которые влияют на определенные сценарии MVC (например: нет доступа к стандартным помощникам MVC Html.)

Омар
источник
1
Примечание для других читателей: это абсолютно правильный ответ, но также ознакомьтесь с дополнительным вкладом Эндрю Нурса ниже, чтобы узнать о возможном обходном пути.
Jordan Gray
38

Я создал метод расширения для помощника WebPages, чтобы получить доступ к помощнику страницы.

public static HtmlHelper GetPageHelper(this System.Web.WebPages.Html.HtmlHelper html)
{
 return ((System.Web.Mvc.WebViewPage) WebPageContext.Current.Page).Html;
}
Джейк Хоффнер
источник
4
Использование:@Html.GetPageHelper().ActionLink("actioname")
deerchao 09
Это работает для меня, но мне пришлось добавить @using System.Web.Mvcи @using System.Web.Mvc.Htmlв файл помощников cshtml внутри App_Code
Томино
1
Почему существует отдельный класс HtmlHelper? Он должен быть одинаковым независимо от того, находится ли он в App_Code или Views. Эпический наполовину реализованный дизайн провалился.
Трийнко
На него ссылается weblogs.asp.net/scottgu/…, который хорошо описывает, как создавать "глобальные" помощники Razor. Итак, если вам нужен HtmlHelperкласс только для целей кодирования, я нашел еще более быстрый способ сделать это с помощью статического класса, Microsoft.Security.Application.Encoderкак в:Encoder.HtmlAttributeEncode(value)
Мэтт Борха
11

У Омара есть правильный ответ, но я хотел кое-что добавить (не стесняйтесь отмечать ответ Омара как ответ).

Мы знали об этом в v1 и не смогли получить отличное исправление в продукте, но Дэвид Эббо (архитектор в команде ASP.Net) опубликовал образец генератора кода Visual Studio, который, по сути, является первым исследованием идеи, которые мы рассматриваем, чтобы эта работа работала должным образом: http://blogs.msdn.com/b/davidebb/archive/2010/10/27/turn-your-razor-helpers-into-reusable-libraries .aspx

Попробуйте и посмотрите, что вы думаете! Сообщите Дэвиду, если у вас есть комментарии, разместив их в его блоге.

Эндрю Стэнтон-Медсестра
источник
2
Есть ли планы исправить это в следующей версии Razor? Я заметил, что это все еще проблема в VS 11 Beta.
Омар
9

Подобно ответу @Jakes:

public static class MvcIntrinsics {
    public static System.Web.Mvc.HtmlHelper Html {
        get { return ((System.Web.Mvc.WebViewPage)WebPageContext.Current.Page).Html; }
    }

    public static System.Web.Mvc.AjaxHelper Ajax {
        get { return ((System.Web.Mvc.WebViewPage)WebPageContext.Current.Page).Ajax; }
    }

    public static System.Web.Mvc.UrlHelper Url {
        get { return ((System.Web.Mvc.WebViewPage)WebPageContext.Current.Page).Url; }
    }

}

Использование:

@MvcIntrinsics.Html.Raw("test")

Источник: Дино Эспозито - Программирование Microsoft ASP.NET MVC

Грег Гам
источник
1
Смешно, что это даже необходимо и что это не просто часть базового класса для представлений в App_Code.
Трийнко
7

Альтернативное решение:

Добавьте это поверх своего файла-помощника по бритве:

@functions {
    public static System.Web.Mvc.HtmlHelper<object> HHtml = ((System.Web.Mvc.WebViewPage)WebPageContext.Current.Page).Html;
}

затем назовите это так:

@HHtml.ActionLink("actionname")
Alex
источник
Это выглядело так, как будто у меня это сработало, но при повторном вызове помощника я получил «значение не попадает в ожидаемый диапазон».
d219 06
3

Мой подход к этому - просто передать страницу в качестве параметра вспомогательному методу. Итак, в вашем примере это будет:

@helper DoSomething(WebViewPage page)
{
    @page.Html.ActionLink("Index", "Home")
}

Затем в вашем представлении Razor, где вам это нужно, назовите его так:

@YourHelperFilename.DoSomething(this)

Это немедленно дает вам доступ к свойствам страницы, подобным Htmlили Urlкоторые у вас обычно есть (и через них к HtmlHelperрасширениям).

В качестве дополнительного преимущества (если вам это нужно) вы также получаете доступ к свойствам экземпляра, таким как page ViewData.

Деяна
источник
3

Для удобства поисковиков я получил ту же ошибку при создании представлений MVC как части библиотеки классов (для повторного использования компонентов). Решение, частично упомянутое выше, заключалось в том, чтобы добавить следующие операторы using вверху файла .cshtml:

@using System.Web.Mvc
@using System.Web.Mvc.Html

Никаких дополнительных действий не требуется.

HockeyJ
источник
Мои помощники в моем .cshtml под App_Code не были распознаны в Intellisense. Добавление @using System.Web.Mvc.Html вверху моего .cshtml под App_code сработало.
Когда так делаю, получаю "Could not load type 'System.Web.WebPages.Instrumentation.InstrumentationService' from assembly 'System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'."при зависании @using System.Web.Mvc. Любые идеи?
JoeBrockhaus
Для меня это не имеет значения
mems
0

Я знаю, что с MVC 3 возникают некоторые проблемы с intellisense. Я думаю, что помощники по-прежнему будут работать, если у вас есть пространство имен, установленное в web.config.

Только что был выпущен MVC 3 RTM. Вы используете эту или бета-версию?

Ли Смит
источник
0

Похоже, ASP.NET MVC устранил эту проблему в VS 2013. См. Этот пост http://aspnet.uservoice.com/forums/41201-asp-net-mvc/suggestions/3670180-support-helper-extensionmethod-this- HtmlHelper-ХТ

Омар
источник
Нет, совсем нет. Intellisense не использует методы расширения, и у меня есть обновление VS2013 5. У меня @using System.Web.Mvc.Htmlвверху файла cshtml в App_Code, но запись @Html .... не раскрывает ни одного из методов расширения, таких как EditorFor. Смешно, что это не работает после двух основных выпусков и сообщений в блогах, в которых утверждается, что это было реализовано. Это не. Фактически, методы расширения не могут работать, поскольку они нацелены на класс System.Web.Mvc.HtmlHelper, а не на класс System.Web.WebPages.HtmlHelper, который предоставляется классом System.Web.WebPages.HelperPage.
Трийнко