2014-11-13 55 views

risposta

1

Net For Nucleo 2,0

using Microsoft.AspNetCore.Mvc.Infrastructure; 

sostituire

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

e

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

Non sono chiaro su dove apporti questa modifica? Potresti aggiungere quell'informazione alla risposta per quelli di noi che non sanno leggere le menti? –

+0

Credo che questo sia per l'implementazione personalizzata @ Html.RenderAction di seguito. – Engin

20

Sì, ViewComponents sarebbe il nuovo modo di fare questo, ma non sono esattamente lo stesso di quello che stava facendo prima @Html.Action però ... per esempio, in MVC5 e, invocando 'azioni bambino' prima sarebbe anche eseguire qualsiasi filtro (ad esempio, se il controller aveva filtri decorati su di essi) dando loro l'aspetto di azioni regolari ... ma questo non è vero con ViewComponents e vengono eseguiti nel contesto della richiesta effettiva ...

Ulteriori informazioni sui componenti della vista: http://www.asp.net/vnext/overview/aspnet-vnext/vc#intro

Aggiornamento: il collegamento è cambiato in

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

+0

Eccellente. Ora possiamo utilizzare le chiamate asincrone quando si invoca "viste parziali" (ora ViewComponents) –

10

ViewComponents sono grandi, ma non così grande per l'Ajax.

Se si perde davvero il metodo @ Html.RenderAction, ecco una rapida implementazione che ho creato per 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; 
     } 
    } 
} 

È possibile richiamare dalla visualizzazione utilizzando uno dei seguenti metodi:

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

Nota:

nome del controller, e il nome di zona opzionalmente, per impostazione predefinita per i valori corrispondenti dalla ActionContext se non fornito.

+0

Tecnicamente, questo codice è una sostituzione di Html.Action(), che restituisce l'azione come valore di stringa. Html.RenderAction() scrive l'azione direttamente nel flusso di risposta e restituisce void. – ReflexiveCode

+0

Ho provato a utilizzare questo codice, ma quando chiamo Html.RenderAction più di una volta viene visualizzato un errore che dice "Si sono verificati uno o più errori. (Un elemento con la stessa chiave è già stato aggiunto. Chiave: System.Object) ". Succede in ControllerActionInvoker.Next che alla fine finisce in "System.ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException" Hai idea di cosa oggetto viene utilizzato come una chiave e come avrei potuto modificare il codice per consentire molteplici usi della stessa richiesta ? –

+0

Avendo lo stesso errore e non riesco a trovare un modo per risolverlo :(. Uno o più errori verificati. (Un elemento con la stessa chiave è già stato aggiunto. Chiave: System.Object) – brechtvhb

2

Per nucleo 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)); 
       } 
      } 
     } 
    } 
} 

Si basa sulla risposta dell'Ariete. Ho corretto ciò che non veniva compilato per 2.0 e ho aggiunto un paio di modifiche. Ci sono 2 valori statici glorificati per l'attuale httpcontext e il corrente actioncontext. Quello per httpcontext è impostato su IHttpContextFactory.Create e ho impostato quello per actioncontext nel codice.

HttpContext è solo un wrapper attorno a HttpContext.Features, quindi se si modifica qualcosa in uno, si modifica anche nell'altro ... Ho reimpostato quello che so riguardo alla fine del try/catch.

Ho rimosso il IUrlHelper dalla cache degli articoli poiché questo valore verrà riutilizzato anche se actionContext per creare urlHelper è diverso (IUrlHelperFactory.GetUrlHelper).

Asp.net core 2.0 presuppone che non lo farai, ci sono buone probabilità che ci siano altre cose memorizzate nella cache, quindi ti consiglio di fare attenzione quando lo usi e semplicemente non farlo se non ne hai bisogno.