ASP.NET Core: ViewBag-Eigenschaft im Konstruktur eines Controllers setzen

ASP.NET Core: ViewBag-Eigenschaft im Konstruktur eines Controllers setzen

In einer MVC-Anwendung kommt es immer wieder vor, dass man bestimmte Daten im gesamten Controller benötigt. Beispielsweise für eine Info-Box oder Sidebar, die man mittels ViewBag/ViewData unabhängig vom Model der jeweiligen Aktionen übergibt. Als objektorientierter Entwickler kommt man hier schnell auf die Idee, die Daten einfach im Konstruktur zu laden. Jedoch ist es nicht möglich, im Konstruktor eines Controllers Eigenschaften zu verändern, die in Relation zur aktuellen Anfrage stehen.

Folgendes Beispiel zeigt dies:

    class MyController : Controller {
        public MyController() {
            ViewBag.GlobalProperty = "SomeValue";
        }

        public IActionResult DoSomething() {
            return View();
        }
    }

ViewBag.GlobalProperty möchten wir nun in einer Razor-Ansicht verwenden. Ob es sich um eine globale Layout-Datei (z.B. für eine Breadcrumb) oder die Ansicht der Aktion DoSomething handelt, spielt keine Rolle: @ViewBag.GlobalProperty ist in beiden Fällen null – Obwohl der Controller aufgerufen und die Eigenschaft dort gesetzt wird. Wie lösen wir das Problem also, ohne eine Hilfsmethode in jeder Action auszuführen und damit das DRY (Dont repeat yourself) Prinzip zu verletzen?

OnActionExecuting schafft Abhilfe

Für diese Fälle gibt es die OnActionExecuting Methode, die jede von Controller erbende Klasse überschreiben kann. Sie wird wie der Name schon sagt vor dem Aufruf einer Aktion des Controllers gefeuert. Hier sind alle Eigenschaften, die sich auf die aktuelle Anfrage beziehen, initialisiert. Man kann daher Problemlos auf Attribute wie ViewBag zugreifen, und diese verändern:

    class MyController :Controller {            
        public override void OnActionExecuted(ActionExecutedContext context) {
            ViewBag.GlobalProperty = "SomeValue";
        }

        public IActionResult DoSomething() {
            return View();
        }
    }

Nun ist der Zugriff auf ViewBag.GlobalProperty problemlos aus jeder Razor-Ansicht heraus möglich.

Alternativ: DataAnnotations-Filter verwenden

Je nach Anwendungsfall ist das überschreiben der OnActionExecuted Methode im Controller nicht immer die beste Lösung. Um etwa die Navigationsstruktur einer Breadcrumb zu erstellen, müsste man die Methoden in jeden Controller kopieren – oder von einem Basis-Controller erben, der diese enthält. Diese Variante ist wenig flexibel, da .NET keine Mehrfachvererbung unterstützt. Besser geeignet ist die Verwendung eines auf den vom EntityFramework bekannten DataAnnotations-Attribute basierten Filtern.

Hier haben wir ebenfalls die Möglichkeit, die OnActionExecuted-Methode des Controllers zu überschreiben. Dies bindet uns nicht an die Limitierungen von Vererbung und lässt sich flexibel einsetzen. Der Aufwand ist kaum höher, wie folgendes Beispiel zeigt:

    public class BreadCrumbAttribute : ResultFilterAttribute {
        readonly string pageName;

        public BreadCrumbAttribute(string pageName) {
            this.pageName = pageName;
        }

        public override void OnResultExecuting(ResultExecutingContext context) {
            var controller = context.Controller as Controller;
            controller.ViewBag.BreadCrumbPageName = pageName;
            base.OnResultExecuting(context);
        }
    }

Nun muss lediglich das DataAnnotation-Attribute [BreadCrumb] für den Controller gesetzt werden:

    [BreadCrumb("Verwaltungsbereich")]
    public class MyController : Controller {
        public IActionResult DoSomething() {
            return View();
        }
    }

Für alle Actions des MyControllers wird ViewBag.BreadCrumbPageName dadurch auf Verwaltungsbereich gesetzt.

Leave a Reply