Korzystanie z rozszerzeń MVC HtmlHelper z deklaratywnych widoków Razor

121

Próbowałem utworzyć deklaratywnego pomocnika Razor w moim folderze App_Code dla projektu MVC 3 RTM.

Problem polegał na tym, że rozszerzenia MVC HtmlHelper, takie jak ActionLink, nie są dostępne. Dzieje się tak, ponieważ skompilowane elementy pomocnicze wywodzą się z właściwości System.Web.WebPages.HelperPagei chociaż ujawniają ją Html, to System.Web.WebPages.HtmlHelperraczej jej typ niż System.Web.Mvc.HtmlHelper.

Przykład rodzaju błędu, który otrzymuję, to:

„System.Web.Mvc.HtmlHelper” nie zawiera definicji „ActionLink” i nie można znaleźć metody rozszerzenia „ActionLink” akceptującej pierwszy argument typu „System.Web.Mvc.HtmlHelper” (czy brakuje dyrektywy using lub odniesienie do zespołu?)

Moim jedynym rozwiązaniem było utworzenie własnej strony HelperPage i zastąpienie właściwości 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; }
    }
}

Następnie na górze każdego pomocnika muszę napisać:

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

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

Czy to ma być takie trudne w MVC 3, czy robię coś źle?

Paul Stovell
źródło
4
Jeśli potrzebujesz również pomocnika Url, możesz dodać ten wiersz kodu do HelperPage: public static UrlHelper Url {get {return new UrlHelper (Html.ViewContext.RequestContext); }}
Marco Staffoli

Odpowiedzi:

42

Spójrz na Marcindodpowiedź na to pytanie. To, czego doświadczasz, to ograniczenie dotyczące umieszczania deklaratywnych widoków w App_Codefolderze.

Umieszczenie pomocników w App_Code działa, ale ma pewne ograniczenia, które mają wpływ na pewne scenariusze MVC (na przykład: brak dostępu do standardowych pomocników MVC Html.)

Omar
źródło
1
Uwaga dla innych czytelników: ta odpowiedź jest absolutnie poprawna, ale sprawdź także dodatkowy wkład Andrew Nurse poniżej, aby uzyskać potencjalne obejście.
Jordan Grey,
38

Stworzyłem metodę rozszerzającą dla pomocnika WebPages, aby uzyskać dostęp do pomocnika strony.

public static HtmlHelper GetPageHelper(this System.Web.WebPages.Html.HtmlHelper html)
{
 return ((System.Web.Mvc.WebViewPage) WebPageContext.Current.Page).Html;
}
Jake Hoffner
źródło
4
Sposób użycia:@Html.GetPageHelper().ActionLink("actioname")
deerchao
To działa dla mnie, ale musiałem dodać @using System.Web.Mvci @using System.Web.Mvc.Htmldo pliku pomocników cshtml wewnątrz App_Code
Tomino
1
Dlaczego istnieje oddzielna klasa HtmlHelper? Powinien być taki sam, niezależnie od tego, czy jest w App_Code, czy w widokach. Epicki, w połowie wdrożony projekt kończy się niepowodzeniem.
Triynko
Zostałem tutaj skierowany przez weblogs.asp.net/scottgu/…, który dobrze się spisuje, opisując, jak tworzyć „globalne” pomocniki Razor. Tak więc, jeśli potrzebujesz tej HtmlHelperklasy tylko do celów kodowania, znalazłem jeszcze szybszy sposób, aby to zrobić, poprzez klasę statyczną, Microsoft.Security.Application.Encoderjak w:Encoder.HtmlAttributeEncode(value)
Matt Borja
11

Omar ma tutaj właściwą odpowiedź, ale chciałem coś dodać (nie krępuj się zaznaczyć odpowiedzi Omara jako odpowiedzi).

Byliśmy tego świadomi w wersji 1 i nie byliśmy w stanie uzyskać doskonałej poprawki w produkcie, ale David Ebbo (architekt w zespole ASP.Net) opublikował próbkę generatora kodu Visual Studio, który jest w zasadzie pierwszą eksploracją rodzaje pomysłów, których szukamy, aby to działało poprawnie: http://blogs.msdn.com/b/davidebb/archive/2010/10/27/turn-your-razor-helpers-into-reusable-libraries .aspx

Wypróbuj to i zobacz, co myślisz! Poinformuj Davida, jeśli masz komentarze, pisząc na jego blogu.

Andrew Stanton-Nurse
źródło
2
Czy są plany, aby to naprawić w następnej wersji Razor? Zauważyłem, że jest to nadal problem w VS 11 Beta.
Omar
9

Podobne do @Jakes answer:

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

}

Stosowanie:

@MvcIntrinsics.Html.Raw("test")

Źródło: Dino Esposito - Programowanie Microsoft ASP.NET MVC

Greg Gum
źródło
1
To śmieszne, że jest to nawet konieczne i że nie są to tylko część klasy bazowej dla widoków w App_Code.
Triynko
7

Alternatywne rozwiązanie:

Dodaj to na górze pliku pomocnika maszynki do golenia:

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

to nazwij to tak:

@HHtml.ActionLink("actionname")
Alex
źródło
Wyglądało na to, że początkowo działało u mnie, ale kiedy dzwoniłem do pomocnika ponownie, otrzymałem „wartość nie mieści się w oczekiwanym zakresie”.
d219
3

Moje podejście do tego polega po prostu na przekazaniu strony jako parametru do metody pomocniczej. W twoim przykładzie wyglądałoby to tak:

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

Następnie w widoku Razor, gdzie jest to potrzebne, nazwij to w ten sposób:

@YourHelperFilename.DoSomething(this)

Dzięki temu natychmiast uzyskasz dostęp do właściwości strony, takich jak Htmllub Urlktóre zwykle posiadasz (a przez to HtmlHelperrozszerzenia).

Dodatkową korzyścią (jeśli tego potrzebujesz) jest dostęp do właściwości instancji, takich jak strona ViewData.

Dejan
źródło
3

Z korzyścią dla wyszukiwarek wystąpił ten sam błąd podczas tworzenia widoków MVC jako części biblioteki klas (do ponownego wykorzystania komponentów). Rozwiązaniem, do którego częściowo nawiązano powyżej, było dodanie następujących instrukcji using na początku pliku .cshtml:

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

Żadna dodatkowa praca nie jest konieczna.

HockeyJ
źródło
Moi pomocnicy w moim .cshtml pod App_Code nie zostali rozpoznani w Intellisense. Dodanie @using System.Web.Mvc.Html na górze mojego .cshtml pod App_code działało.
Kiedy to robię, "Could not load type 'System.Web.WebPages.Instrumentation.InstrumentationService' from assembly 'System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'."unoszę się w powietrzu @using System.Web.Mvc. Jakieś pomysły?
JoeBrockhaus
Dla mnie to nie ma znaczenia
memy
0

Wiem, że istnieją pewne problemy z inteligencją w MVC 3. Myślę, że pomocnicy będą nadal działać, jeśli masz przestrzeń nazw ustawioną w web.config.

MVC 3 RTM właśnie został wydany, czy używasz tego, czy wersji beta?

Lee Smith
źródło
0

Wygląda na to, że ASP.NET MVC rozwiązało ten problem w VS 2013. Zobacz ten post http://aspnet.uservoice.com/forums/41201-asp-net-mvc/suggestions/3670180-support-helper-extensionmethod-this- htmlhelper-ht

Omar
źródło
Nie, wcale nie. Intellisense nie odbiera metod rozszerzających, a mam VS2013 aktualizację 5. Mam @using System.Web.Mvc.Htmlna górze pliku cshtml w App_Code, ale pisząc @Html .... nie ujawnia żadnej z metod rozszerzających, takich jak EditorFor. To absurdalne, że to nie działa po 2 głównych wydaniach i postach na blogu, które twierdzą, że zostało zaimplementowane. To nie jest. W rzeczywistości metody rozszerzające nie mogą działać, ponieważ są one przeznaczone dla klasy System.Web.Mvc.HtmlHelper, a nie klasy System.Web.WebPages.HtmlHelper, która jest udostępniana przez klasę System.Web.WebPages.HelperPage.
Triynko