2014-11-13 35 views

Odpowiedz

1

NET rdzenia 2,0

using Microsoft.AspNetCore.Mvc.Infrastructure; 

wymienić

// var actionSelector = GetServiceOrFail<IActionSelectorDecisionTreeProvider>(currentHttpContext); 
var actionSelector = GetServiceOrFail<IActionDescriptorCollectionProvider>(currentHttpContext); 

i

// var actionDescriptor = actionSelector.DecisionTree.Select(routeValues).First(); 
var actionDescriptor = actionSelector.ActionDescriptors.Items.Where(i => i.RouteValues["Controller"] == controller && i.RouteValues["Action"] == action).First(); 
+3

Nie jestem pewien, gdzie dokonujesz tej zmiany? Czy możesz dodać te informacje do odpowiedzi dla tych z nas, którzy nie potrafią czytać w myślach? –

+0

Uważam, że dotyczy to niestandardowej implementacji @ Html.RenderAction poniżej. – Engin

20

Tak, ViewComponents byłby nowy sposób to zrobić, ale nie są one dokładnie to samo, co robił przed @Html.Action ... choć na przykład w MVC5 i wcześniejszych powołując "działań dziecka również Wykonaj dowolne filtry (na przykład, jeśli kontroler miał ozdobione filtry), nadając im wygląd regularnych działań ... ale to nie jest prawdą w ViewComponents i są one wykonywane w kontekście aktualnego żądania ...

Więcej informacji o komponentach widoku: http://www.asp.net/vnext/overview/aspnet-vnext/vc#intro

Aktualizacja: link zmieniono na

https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components

+0

Doskonały. Teraz możemy używać asynchronicznych wywołań podczas wywoływania "częściowych widoków" (teraz ViewComponents). –

10

ViewComponents są świetne, ale nie tak dobre dla Ajax.

Jeśli naprawdę brakuje Ci metody @ Html.RenderAction, oto krótka implementacja, którą wyrzuciłem razem dla AspNetCore.

using System; 
using System.IO; 
using System.Linq; 
using System.Threading.Tasks; 
using Microsoft.AspNetCore.Html; 
using Microsoft.AspNetCore.Http; 
using Microsoft.AspNetCore.Http.Extensions; 
using Microsoft.AspNetCore.Mvc; 
using Microsoft.AspNetCore.Mvc.Controllers; 
using Microsoft.AspNetCore.Mvc.Internal; 
using Microsoft.AspNetCore.Mvc.Infrastructure; 
using Microsoft.AspNetCore.Mvc.Razor; 
using Microsoft.AspNetCore.Mvc.Rendering; 
using Microsoft.AspNetCore.Routing; 

namespace Microsoft.AspNetCore.Mvc.Rendering { 

    public static class HtmlHelperViewExtensions 
    { 

     public static IHtmlContent RenderAction(this IHtmlHelper helper, string action, object parameters = null) 
     { 
      var controller = (string)helper.ViewContext.RouteData.Values["controller"]; 

      return RenderAction(helper, action, controller, parameters); 
     } 

     public static IHtmlContent RenderAction(this IHtmlHelper helper, string action, string controller, object parameters = null) 
     { 
      var area = (string)helper.ViewContext.RouteData.Values["area"]; 

      return RenderAction(helper, action, controller, area, parameters); 
     } 

     public static IHtmlContent RenderAction(this IHtmlHelper helper, string action, string controller, string area, object parameters = null) 
     { 
      if (action == null) 
       throw new ArgumentNullException("action"); 

      if (controller == null) 
       throw new ArgumentNullException("controller"); 

      if (area == null) 
       throw new ArgumentNullException("area"); 

      var task = RenderActionAsync(helper, action, controller, area, parameters); 

      return task.Result; 
     } 

     private static async Task<IHtmlContent> RenderActionAsync(this IHtmlHelper helper, string action, string controller, string area, object parameters = null) 
     { 
      // fetching required services for invocation 
      var currentHttpContext = helper.ViewContext?.HttpContext; 
      var httpContextFactory = GetServiceOrFail<IHttpContextFactory>(currentHttpContext); 
      var actionInvokerFactory = GetServiceOrFail<IActionInvokerFactory>(currentHttpContext); 
      var actionSelector = GetServiceOrFail<IActionSelectorDecisionTreeProvider>(currentHttpContext); 

      // creating new action invocation context 
      var routeData = new RouteData(); 
      var routeParams = new RouteValueDictionary(parameters ?? new { }); 
      var routeValues = new RouteValueDictionary(new { area = area, controller = controller, action = action }); 
      var newHttpContext = httpContextFactory.Create(currentHttpContext.Features); 

      newHttpContext.Response.Body = new MemoryStream(); 

      foreach (var router in helper.ViewContext.RouteData.Routers) 
       routeData.PushState(router, null, null); 

      routeData.PushState(null, routeValues, null); 
      routeData.PushState(null, routeParams, null); 

      var actionDescriptor = actionSelector.DecisionTree.Select(routeValues).First(); 
      var actionContext = new ActionContext(newHttpContext, routeData, actionDescriptor); 

      // invoke action and retreive the response body 
      var invoker = actionInvokerFactory.CreateInvoker(actionContext); 
      string content = null; 

      await invoker.InvokeAsync().ContinueWith(task => { 
       if (task.IsFaulted) 
       { 
        content = task.Exception.Message; 
       } 
       else if (task.IsCompleted) 
       { 
        newHttpContext.Response.Body.Position = 0; 
        using (var reader = new StreamReader(newHttpContext.Response.Body)) 
         content = reader.ReadToEnd(); 
       } 
      }); 

      return new HtmlString(content); 
     } 

     private static TService GetServiceOrFail<TService>(HttpContext httpContext) 
     { 
      if (httpContext == null) 
       throw new ArgumentNullException(nameof(httpContext)); 

      var service = httpContext.RequestServices.GetService(typeof(TService)); 

      if (service == null) 
       throw new InvalidOperationException($"Could not locate service: {nameof(TService)}"); 

      return (TService)service; 
     } 
    } 
} 

Można powołać się od widoku za pomocą jednej z następujących metod:

@Html.RenderAction("action", "controller", "area", new { id = 1}) 
@Html.RenderAction("action", "controller", new { id = 1}) 
@Html.RenderAction("action", new { id = 1}) 

Uwaga:

nazwa kontrolera i ewentualnie nazwa obszaru, będzie domyślnie odpowiednimi wartościami z ActionContext jeżeli nie przewidziane.

+0

Technicznie, ten kod jest zamiennikiem dla Html.Action(), które zwraca działanie jako wartość ciągu. Html.RenderAction() zapisuje akcję bezpośrednio do strumienia odpowiedzi i zwraca nieważne. – ReflexiveCode

+0

Próbowałem użyć tego kodu, ale kiedy wywołuję Html.RenderAction więcej niż jeden raz, pojawia się komunikat "Wystąpił jeden lub więcej błędów. (Element z tym samym kluczem został już dodany. Klucz: System.Object) ". Zdarza się ControllerActionInvoker.Next który ostatecznie kończy się w „System.ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException” masz jakiś pomysł, co przedmiot jest używany jako klucz i jak mogę zmienić kod, aby umożliwić wielu zastosowań w tym samym żądanie ? –

+0

Mając ten sam błąd i nie można znaleźć sposobu na jego rozwiązanie :( Wystąpił jeden lub więcej błędów. (Dodano pozycję z tym samym kluczem: Klucz: System.Object) – brechtvhb

2

do rdzeń asp.net 2

using Microsoft.AspNetCore.Html; 
using Microsoft.AspNetCore.Http; 
using Microsoft.AspNetCore.Mvc.Infrastructure; 
using Microsoft.AspNetCore.Routing; 
using Microsoft.Extensions.DependencyInjection; 
using System; 
using System.IO; 
using System.Threading.Tasks; 

namespace Microsoft.AspNetCore.Mvc.Rendering 
{ 
    public static class HtmlHelperViewExtensions 
    { 
     public static IHtmlContent Action(this IHtmlHelper helper, string action, object parameters = null) 
     { 
      var controller = (string)helper.ViewContext.RouteData.Values["controller"]; 

      return Action(helper, action, controller, parameters); 
     } 

     public static IHtmlContent Action(this IHtmlHelper helper, string action, string controller, object parameters = null) 
     { 
      var area = (string)helper.ViewContext.RouteData.Values["area"]; 

      return Action(helper, action, controller, area, parameters); 
     } 

     public static IHtmlContent Action(this IHtmlHelper helper, string action, string controller, string area, object parameters = null) 
     { 
      if (action == null) 
       throw new ArgumentNullException("action"); 

      if (controller == null) 
       throw new ArgumentNullException("controller"); 


      var task = RenderActionAsync(helper, action, controller, area, parameters); 

      return task.Result; 
     } 

     private static async Task<IHtmlContent> RenderActionAsync(this IHtmlHelper helper, string action, string controller, string area, object parameters = null) 
     { 
      // fetching required services for invocation 
      var serviceProvider = helper.ViewContext.HttpContext.RequestServices; 
      var actionContextAccessor = helper.ViewContext.HttpContext.RequestServices.GetRequiredService<IActionContextAccessor>(); 
      var httpContextAccessor = helper.ViewContext.HttpContext.RequestServices.GetRequiredService<IHttpContextAccessor>(); 
      var actionSelector = serviceProvider.GetRequiredService<IActionSelector>(); 

      // creating new action invocation context 
      var routeData = new RouteData(); 
      foreach (var router in helper.ViewContext.RouteData.Routers) 
      { 
       routeData.PushState(router, null, null); 
      } 
      routeData.PushState(null, new RouteValueDictionary(new { controller = controller, action = action, area = area }), null); 
      routeData.PushState(null, new RouteValueDictionary(parameters ?? new { }), null); 

      //get the actiondescriptor 
      RouteContext routeContext = new RouteContext(helper.ViewContext.HttpContext) { RouteData = routeData }; 
      var candidates = actionSelector.SelectCandidates(routeContext); 
      var actionDescriptor = actionSelector.SelectBestCandidate(routeContext, candidates); 

      var originalActionContext = actionContextAccessor.ActionContext; 
      var originalhttpContext = httpContextAccessor.HttpContext; 
      try 
      { 
       var newHttpContext = serviceProvider.GetRequiredService<IHttpContextFactory>().Create(helper.ViewContext.HttpContext.Features); 
       if (newHttpContext.Items.ContainsKey(typeof(IUrlHelper))) 
       { 
        newHttpContext.Items.Remove(typeof(IUrlHelper)); 
       } 
       newHttpContext.Response.Body = new MemoryStream(); 
       var actionContext = new ActionContext(newHttpContext, routeData, actionDescriptor); 
       actionContextAccessor.ActionContext = actionContext; 
       var invoker = serviceProvider.GetRequiredService<IActionInvokerFactory>().CreateInvoker(actionContext); 
       await invoker.InvokeAsync(); 
       newHttpContext.Response.Body.Position = 0; 
       using (var reader = new StreamReader(newHttpContext.Response.Body)) 
       { 
        return new HtmlString(reader.ReadToEnd()); 
       } 
      } 
      catch (Exception ex) 
      { 
       return new HtmlString(ex.Message); 
      } 
      finally 
      { 
       actionContextAccessor.ActionContext = originalActionContext; 
       httpContextAccessor.HttpContext = originalhttpContext; 
       if (helper.ViewContext.HttpContext.Items.ContainsKey(typeof(IUrlHelper))) 
       { 
        helper.ViewContext.HttpContext.Items.Remove(typeof(IUrlHelper)); 
       } 
      } 
     } 
    } 
} 

Opiera się na odpowiedzi od Barana. Poprawiłem to, co nie było kompilacji dla wersji 2.0 i dodałem kilka poprawek. Istnieją 2 gloryfikowane wartości statyczne dla bieżącego httpcontext i bieżącego actioncontext. Ta dla httpcontext jest ustawiona na IHttpContextFactory.Create i ustawiam dla actioncontext w kodzie.

HttpContext to po prostu wrapper dookoła HttpContext.Features, więc jeśli zmienisz coś w jednym, to zmieni się również w drugim ... Zresetowałem to, co wiem o tym w końcu try/catch.

Usunąłem IUrlHelper z pamięci podręcznej elementów, ponieważ ta wartość będzie ponownie użyta, nawet jeśli działanieContext do zbudowania urlHelper jest inne (IUrlHelperFactory.GetUrlHelper).

Asp.net core 2.0 zakłada, że ​​tego nie zrobisz, istnieje duża szansa, że ​​istnieją inne buforowane rzeczy, więc zalecam ostrożność podczas korzystania z tego i po prostu nie rób tego, jeśli nie musisz.