¿Cómo establecer propiedades ViewBag para todas las Vistas sin usar una clase base para Controladores?

En el pasado, he pegado propiedades comunes, como el usuario actual, en ViewData / ViewBag de forma global haciendo que todos los controladores hereden de un controlador base común.

Esto me permitió usar IoC en el controlador base y no solo extenderme a global compartido para esos datos.

Me pregunto si existe una forma alternativa de insertar este tipo de código en la tubería de MVC.

No lo intenté, pero podría ver registrar sus vistas y luego configurar los datos de la vista durante el proceso de activación.

Debido a que las vistas se registran sobre la marcha, la syntax de registro no le ayuda a conectarse al evento Activated , por lo que deberá configurarlo en un Module :

 class SetViewBagItemsModule : Module { protected override void AttachToComponentRegistration( IComponentRegistration registration, IComponentRegistry registry) { if (typeof(WebViewPage).IsAssignableFrom(registration.Activator.LimitType)) { registration.Activated += (s, e) => { ((WebViewPage)e.Instance).ViewBag.Global = "global"; }; } } } 

Esta podría ser una de esas sugerencias de “solo una herramienta es un martillo”; puede haber formas más simples habilitadas con MVC para llegar a él.

Editar: alternativa, menos enfoque de código: solo adjuntar al controlador

 public class SetViewBagItemsModule: Module { protected override void AttachToComponentRegistration(IComponentRegistry cr, IComponentRegistration reg) { Type limitType = reg.Activator.LimitType; if (typeof(Controller).IsAssignableFrom(limitType)) { registration.Activated += (s, e) => { dynamic viewBag = ((Controller)e.Instance).ViewBag; viewBag.Config = e.Context.Resolve(); viewBag.Identity = e.Context.Resolve(); }; } } } 

Edición 2: otro enfoque que funciona directamente desde el código de registro del controlador:

 builder.RegisterControllers(asm) .OnActivated(e => { dynamic viewBag = ((Controller)e.Instance).ViewBag; viewBag.Config = e.Context.Resolve(); viewBag.Identity = e.Context.Resolve(); }); 

La mejor forma es usar ActionFilterAttribute y registrar su clase personalizada en su global. asax (Application_Start)

 public class UserProfilePictureActionFilter : ActionFilterAttribute { public override void OnResultExecuting(ResultExecutingContext filterContext) { filterContext.Controller.ViewBag.IsAuthenticated = MembershipService.IsAuthenticated; filterContext.Controller.ViewBag.IsAdmin = MembershipService.IsAdmin; var userProfile = MembershipService.GetCurrentUserProfile(); if (userProfile != null) { filterContext.Controller.ViewBag.Avatar = userProfile.Picture; } } } 

registra tu clase personalizada en tu global. asax (Application_Start)

 protected void Application_Start() { AreaRegistration.RegisterAllAreas(); GlobalFilters.Filters.Add(new UserProfilePictureActionFilter(), 0); } 

Entonces puedes usarlo en todas las vistas

 @ViewBag.IsAdmin @ViewBag.IsAuthenticated @ViewBag.Avatar 

También hay otra manera

Crear un método de extensión en HtmlHelper

 [Extension()] public string MyTest(System.Web.Mvc.HtmlHelper htmlHelper) { return "This is a test"; } 

Entonces puedes usarlo en todas las vistas

 @Html.MyTest() 

Como las propiedades de ViewBag están vinculadas, por definición, a la presentación de la vista y a cualquier lógica de visualización ligera que pueda ser necesaria, crearía una WebViewPage base y establecería las propiedades en la inicialización de la página. Es muy similar al concepto de un controlador base para lógica repetida y funcionalidad común, pero para sus puntos de vista:

  public abstract class ApplicationViewPage : WebViewPage { protected override void InitializePage() { SetViewBagDefaultProperties(); base.InitializePage(); } private void SetViewBagDefaultProperties() { ViewBag.GlobalProperty = "MyValue"; } } 

Y luego en \Views\Web.config , establezca la propiedad pageBaseType :

            

La publicación de Brandon tiene razón en el dinero. De hecho, me gustaría ir un paso más allá y decir que solo debe agregar sus objetos comunes como propiedades de la base WebViewPage para que no tenga que moldear elementos desde la ViewBag en cada vista. Hago mi configuración de CurrentUser de esta manera.

Puede usar un resultado de acción personalizado:

 public class GlobalView : ActionResult { public override void ExecuteResult(ControllerContext context) { context.Controller.ViewData["Global"] = "global"; } } 

O incluso un ActionFilter:

 public class GlobalView : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { filterContext.Result = new ViewResult() {ViewData = new ViewDataDictionary()}; base.OnActionExecuting(filterContext); } } 

Tenía un proyecto de MVC 2 abierto, pero ambas técnicas aún se aplican con cambios menores.

Si desea verificar el tiempo de comstackción y intellisense para las propiedades en sus vistas, entonces ViewBag no es el camino a seguir.

Considere una clase de BaseViewModel y haga que sus otros modelos de vista hereden de esta clase, por ejemplo:

Base ViewModel

 public class BaseViewModel { public bool IsAdmin { get; set; } public BaseViewModel(IUserService userService) { IsAdmin = userService.IsAdmin; } } 

Ver ViewModel específico

 public class WidgetViewModel : BaseViewModel { public string WidgetName { get; set;} } 

Ahora el código de vista puede acceder a la propiedad directamente en la vista

 

Is Admin: @Model.IsAdmin

No tiene que meterse en acciones o cambiar el modelo, solo use un controlador base y eche el controlador existente desde el contexto de vista de diseño.

Cree un controlador base con los datos comunes deseados (título / página / ubicación, etc.) e inicialización de la acción …

 public abstract class _BaseController:Controller { public Int32 MyCommonValue { get; private set; } protected override void OnActionExecuting(ActionExecutingContext filterContext) { MyCommonValue = 12345; base.OnActionExecuting(filterContext); } } 

Asegúrese de que cada controlador use el controlador base …

 public class UserController:_BaseController {... 

Eche el controlador base existente desde el contexto de vista en su página _Layout.cshml

 @{ var myController = (_BaseController)ViewContext.Controller; } 

Ahora puede consultar los valores en su controlador base desde su página de diseño.

 @myController.MyCommonValue 

He encontrado que el siguiente enfoque es el más eficiente y brinda un control excelente utilizando el archivo _ViewStart.chtml y las declaraciones condicionales cuando sea necesario:

_ ViewStart :

 @{ Layout = "~/Views/Shared/_Layout.cshtml"; var CurrentView = ViewContext.Controller.ValueProvider.GetValue("controller").RawValue.ToString(); if (CurrentView == "ViewA" || CurrentView == "ViewB" || CurrentView == "ViewC") { PageData["Profile"] = db.GetUserAccessProfile(); } } 

ViewA :

 @{ var UserProfile= PageData["Profile"] as List; } 

Nota :

PageData funcionará perfectamente en Views; sin embargo, en el caso de una Vista parcial, deberá pasarse de la vista al secundario parcial.