如何在 ExecuteResultAsync 中访问 ViewDataDictionary?

How to access ViewDataDictionary inside ExecuteResultAsync?

简介

我正在尝试使用扩展方法装饰 Action - ViewResult。目标是能够在 Controller 之外的 Decorator 的使用范围内设置 ViewData。但是,我只能访问 TempData,而我想在方法 ExecuteResultAsync 中访问 ViewData

问题

How to access ViewData from ExecuteResultAsync within use of accessing to ActionContext? Is this the following scenario at all possible?

场景

所以不要这样写:

public async Task<IActionResult> ChangePassword()
{
    ViewData["Title"] = "Change Password";
    return PartialView("_ChangePasswordPartial",...);
}

我正在尝试让它像这样工作:

public async Task<IActionResult> ChangePassword()
{
    return PartialView("_ChangePasswordPartial",...).WithTitle("Change Password");
}

扩展方法:

public static IActionResult WithTitle(this IActionResult action, string modalTitle)
{
    return new ModalTitleDecorator(action,modalTitle);
}

装饰器:

public class ModalTitleDecorator : ActionResult
{
     private readonly IActionResult _actionResult;
     private readonly string _modalTitle;

     public ModalTitleDecorator(IActionResult action, string modalTitle)
     {
         _actionResult = action;
         _modalTitle = modalTitle;
     }

      public override async Task ExecuteResultAsync(ActionContext context)
      {

          var tempData = context.HttpContext.RequestServices.GetService<ITempDataDictionaryFactory>()
                   .GetTempData(context.HttpContext);

         //always null...I assume that ViewData is not available according to HttpContext. 
         //TempData is available probably according to passing into next request.
         var viewData = context.HttpContext.RequestServices.GetService<ViewDataDictionary>();

         //something like this would be great
         ViewData["Title"] = _modalTitle;

         await _actionResult.ExecuteResultAsync(context);
      }
}

对于 ViewData,您无法通过 context.HttpContext.RequestServices.GetService 解决它。

/// <summary>
/// Gets or sets <see cref="ViewDataDictionary"/> used by <see cref="ViewResult"/> and <see cref="ViewBag"/>.
/// </summary>
/// <remarks>
/// By default, this property is intiailized when <see cref="Controllers.IControllerActivator"/> activates
/// controllers.
/// <para>
/// This property can be accessed after the controller has been activated, for example, in a controller action
/// or by overriding <see cref="OnActionExecuting(ActionExecutingContext)"/>.
/// </para>
/// <para>
/// This property can be also accessed from within a unit test where it is initialized with
/// <see cref="EmptyModelMetadataProvider"/>.
/// </para>
/// </remarks>
[ViewDataDictionary]
public ViewDataDictionary ViewData

如上声明,您只能在ControllerOnActionExecuting(ActionExecutingContext)中访问ViewData。您无法通过已解析的服务访问它。

您可以尝试通过反映 IActionResult 来访问 ViewData

尝试

public static class ActionResultExtension
{
    public static IActionResult WithTitle(this IActionResult action, string modalTitle)
    {
        return new ModalTitleDecorator(action, modalTitle);
    }
}

public class ModalTitleDecorator : PartialViewResult
{
    private readonly IActionResult _actionResult;
    private readonly string _modalTitle;

    public ModalTitleDecorator(IActionResult action, string modalTitle)
    {
        _actionResult = action;
        _modalTitle = modalTitle;
    }

    public override async Task ExecuteResultAsync(ActionContext context)
    {
        ViewDataDictionary viewData = _actionResult
                                    .GetType()
                                    .GetProperty("ViewData")
                                    .GetValue(_actionResult) as ViewDataDictionary;
        if (viewData != null)
        {
            viewData["Title"] = _modalTitle;
        }
        await _actionResult.ExecuteResultAsync(context);
    }
}