6

Mam metodę usuwania obiektu. Usunięcie nie jest właścicielem widoku i jest przyciskiem "Usuń" w "EditReport". Po pomyślnym usunięciu przekierowania w "Raporcie".Jak nadal używać ModelState z RedirectToAction w ASP.NET MVC 6?

[HttpPost] 
[Route("{reportId:int}")] 
[ValidateAntiForgeryToken] 
public IActionResult DeleteReport(int reportId) 
{ 
    var success = _reportService.DeleteReportControl(reportId); 
    if (success == false) 
    { 
     ModelState.AddModelError("Error", "Messages"); 
     return RedirectToAction("EditReport"); 
    } 
    ModelState.AddModelError("OK", "Messages"); 
    return RedirectToAction("Report"); 
} 

W środowisku ASP.NET MVC 5 używam następujących atrybutów do zapisania parametru ModelState między metodami. Wziąłem stąd: https://stackoverflow.com/a/12024227/3878213

public class SetTempDataModelStateAttribute : ActionFilterAttribute 
{ 
    public override void OnActionExecuted(ActionExecutedContext filterContext) 
    { 
     base.OnActionExecuted(filterContext);   
     filterContext.Controller.TempData["ModelState"] = 
      filterContext.Controller.ViewData.ModelState; 
    } 
} 

public class RestoreModelStateFromTempDataAttribute : ActionFilterAttribute 
{ 
    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     base.OnActionExecuting(filterContext); 
     if (filterContext.Controller.TempData.ContainsKey("ModelState")) 
     { 
      filterContext.Controller.ViewData.ModelState.Merge(
       (ModelStateDictionary)filterContext.Controller.TempData["ModelState"]); 
     } 
    } 
} 

Ale w ASP.NET MVC 6 RC 1 (podstawowa ASP.NET 1.0), ten kod nie działa.

Błąd w filterContext.Controller nie zawiera definicji dla TempData i ViewData.

Odpowiedz

4

Dzięki answer, zdałem sobie sprawę, że potrzeba tworzenia własnego kodu ASP.NET rdzenia 1.0 (Full .NET Framework 4.6.2)

public class SetTempDataModelStateAttribute : ActionFilterAttribute 
{ 
    public override void OnActionExecuted(ActionExecutedContext filterContext) 
    { 
     base.OnActionExecuted(filterContext); 

     var controller = filterContext.Controller as Controller; 
     var modelState = controller?.ViewData.ModelState; 
     if (modelState != null) 
     { 
      var listError = modelState.Where(x => x.Value.Errors.Any()) 
       .ToDictionary(m => m.Key, m => m.Value.Errors 
       .Select(s => s.ErrorMessage) 
       .FirstOrDefault(s => s != null)); 
      controller.TempData["ModelState"] = JsonConvert.SerializeObject(listError); 
     } 
    } 
} 
public class RestoreModelStateFromTempDataAttribute : ActionFilterAttribute 
{ 
    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     base.OnActionExecuting(filterContext); 

     var controller = filterContext.Controller as Controller; 
     var tempData = controller?.TempData?.Keys; 
     if (controller != null && tempData != null) 
     { 
      if (tempData.Contains("ModelState")) 
      { 
       var modelStateString = controller.TempData["ModelState"].ToString(); 
       var listError = JsonConvert.DeserializeObject<Dictionary<string, string>>(modelStateString); 
       var modelState = new ModelStateDictionary(); 
       foreach (var item in listError) 
       { 
        modelState.AddModelError(item.Key, item.Value ?? ""); 
       } 

       controller.ViewData.ModelState.Merge(modelState); 
      } 
     } 
    } 
} 

Asynchroniczna wersja kodu ASP.NET Core 1.0 (pełna wersja .NET Framework 4.6.2)

public class SetTempDataModelStateAttribute : ActionFilterAttribute 
    { 
     public override async Task OnActionExecutionAsync(ActionExecutingContext filterContext, ActionExecutionDelegate next) 
     { 
      await base.OnActionExecutionAsync(filterContext, next); 

      var controller = filterContext.Controller as Controller; 
      var modelState = controller?.ViewData.ModelState; 
      if (modelState != null) 
      { 
       var listError = modelState.Where(x => x.Value.Errors.Any()) 
        .ToDictionary(m => m.Key, m => m.Value.Errors 
        .Select(s => s.ErrorMessage) 
        .FirstOrDefault(s => s != null)); 
       var listErrorJson = await Task.Run(() => JsonConvert.SerializeObject(listError)); 
       controller.TempData["ModelState"] = listErrorJson; 
      } 
      await next(); 
     } 
    } 
public class RestoreModelStateFromTempDataAttribute : ActionFilterAttribute 
    { 
     public override async Task OnActionExecutionAsync(ActionExecutingContext filterContext, ActionExecutionDelegate next) 
     { 
      await base.OnActionExecutionAsync(filterContext, next); 

      var controller = filterContext.Controller as Controller; 
      var tempData = controller?.TempData?.Keys; 
      if (controller != null && tempData != null) 
      { 
       if (tempData.Contains("ModelState")) 
       { 
        var modelStateString = controller.TempData["ModelState"].ToString(); 
        var listError = await Task.Run(() => 
         JsonConvert.DeserializeObject<Dictionary<string, string>>(modelStateString)); 
        var modelState = new ModelStateDictionary(); 
        foreach (var item in listError) 
        { 
         modelState.AddModelError(item.Key, item.Value ?? ""); 
        } 

        controller.ViewData.ModelState.Merge(modelState); 
       } 
      } 
      await next(); 
     } 
    } 
+1

Należy sprawdzić, czy nie pojawiają się błędy w linii 'SetTempDataModelStateAttribute' var listError = modelState.ToDictionary (m => m.Key, m => m.Value.Errors' powinno być 'modelState.Where (x => x.Value.Errors.Any()). ToDictionary (m ...' –

+1

Użycie 'czekaj na następny() ; 'jest tutaj niepoprawne, ponieważ powoduje, że metoda jest wielokrotnie trafiana. –

3

Poprawka do kompilacji kodu znajduje się poniżej, ale wygląda na to, że ASP.NET Core nie obsługuje serializacji stanu modelu (z powodu wyjątków, które nigdy nie są serializowane).

W związku z tym nie można przekształcić stanu modelu w TempData. I jak wyjaśniono w this GitHub issue, wydaje się, że nie ma planów zmiany tego zachowania.


Obiekt Controller w ActionExecutingContext jest typu object. Wynika to z faktu, że kontrolery w środowisku ASP.NET Core nie muszą dziedziczyć po Controller, więc nie ma dla nich wspólnego typu podstawowego.

Aby uzyskać dostęp do właściwości TempData, należy najpierw przesłać ją do Controller. Atrybuty mógłby wyglądać następująco następnie:

public class SetTempDataModelStateAttribute : ActionFilterAttribute 
{ 
    public override void OnActionExecuted(ActionExecutedContext filterContext) 
    { 
     base.OnActionExecuted(filterContext); 

     Controller controller = filterContext.Controller as Controller; 
     if (controller != null) 
     { 
      controller.TempData["ModelState"] = controller.ViewData.ModelState; 
     } 
    } 
} 

public class RestoreModelStateFromTempDataAttribute : ActionFilterAttribute 
{ 
    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     base.OnActionExecuting(filterContext); 

     Controller controller = filterContext.Controller as Controller; 
     if (controller != null & controller.TempData.ContainsKey("ModelState")) 
     { 
      controller.ViewData.ModelState.Merge(
       (ModelStateDictionary)controller.TempData["ModelState"]); 
     } 
    } 
} 
+0

Dodaj do projektu Sesje. Chociaż nie mogę rozwiązać problemu z błędem: 'InvalidOperationException: 'Microsoft.AspNet.Mvc.ViewFeatures.SessionStateTempDataProvider' nie może serializować obiektu typu" Microsoft.AspNet.Mvc.ModelBinding.ModelStateDictionary "do stanu sesji. –

+0

Wygląda na to, że środowisko ASP.NET Core nie obsługuje serializacji stanu modelu i nie planuje tego zmieniać. Zaktualizowałem swoją odpowiedź, aby to odzwierciedlić. – poke

+0

poprawnie konwertuje ModelState w ciąg (json) i przechowywać w TempData? –

Powiązane problemy