Как установить свойства ViewBag для всех представлений без использования базового класса для контроллеров?
в прошлом я застрял общие свойства, такие как текущий пользователь, на ViewData/ViewBag в глобальном режиме, имея все контроллеры наследуют от общего базового контроллера.
Это позволило мне использовать IoC на базовом контроллере, а не просто обращаться к глобальным общим данным для таких данных.
Мне интересно, есть ли альтернативный способ вставки такого кода в конвейер MVC?
8 ответов:
Я не пробовал, но вы можете посмотреть на Регистрация вид и затем установка данных представления во время процесса активации.
поскольку представления регистрируются на лету, синтаксис регистрации не помогает вам подключиться к
Activated
событие, так что вам нужно будет настроить его в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"; }; } } }
Это может быть одно из тех предложений типа"только инструмент-молоток" от меня; могут быть более простые способы с поддержкой MVC оно.
Edit: альтернативный, менее кодовый подход-просто прикрепите к контроллеру
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<Config>(); viewBag.Identity = e.Context.Resolve<IIdentity>(); }; } } }
Edit 2: другой подход, который работает непосредственно из регистрационного кода контроллера:
builder.RegisterControllers(asm) .OnActivated(e => { dynamic viewBag = ((Controller)e.Instance).ViewBag; viewBag.Config = e.Context.Resolve<Config>(); viewBag.Identity = e.Context.Resolve<IIdentity>(); });
лучший способ-использовать ActionFilterAttribute и зарегистрировать свой пользовательский класс в глобальной. 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; } } }
зарегистрируйте свой пользовательский класс в своем глобальном. asax (Application_Start)
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); GlobalFilters.Filters.Add(new UserProfilePictureActionFilter(), 0); }
затем вы можете использовать его во всех видах
@ViewBag.IsAdmin @ViewBag.IsAuthenticated @ViewBag.Avatar
также есть другой способ
создание метода расширения на HtmlHelper
[Extension()] public string MyTest(System.Web.Mvc.HtmlHelper htmlHelper) { return "This is a test"; }
затем вы можете использовать его во всех просмотров
@Html.MyTest()
поскольку свойства ViewBag по определению привязаны к представлению представления и любой логике светового представления, которая может потребоваться,Я бы создал базовую WebViewPage и установите свойства при инициализации страницы. Это очень похоже на концепцию базового контроллера для повторной логики и общей функциональности, но для ваших представлений:
public abstract class ApplicationViewPage<T> : WebViewPage<T> { protected override void InitializePage() { SetViewBagDefaultProperties(); base.InitializePage(); } private void SetViewBagDefaultProperties() { ViewBag.GlobalProperty = "MyValue"; } }
а потом в
\Views\Web.config
, выберитеpageBaseType
свойства:<system.web.webPages.razor> <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> <pages pageBaseType="MyNamespace.ApplicationViewPage"> <namespaces> <add namespace="System.Web.Mvc" /> <add namespace="System.Web.Mvc.Ajax" /> <add namespace="System.Web.Mvc.Html" /> <add namespace="System.Web.Routing" /> </namespaces> </pages> </system.web.webPages.razor>
сообщение Брэндона прямо на деньги. На самом деле, я бы сделал этот шаг дальше и сказал, что вы должны просто добавить свои общие объекты как свойства на base WebViewPage таким образом, вам не нужно бросать элементы из ViewBag в каждом отдельном представлении. Я делаю свою настройку CurrentUser таким образом.
вы можете использовать пользовательский ActionResult:
public class GlobalView : ActionResult { public override void ExecuteResult(ControllerContext context) { context.Controller.ViewData["Global"] = "global"; } }
или даже ActionFilter:
public class GlobalView : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { filterContext.Result = new ViewResult() {ViewData = new ViewDataDictionary()}; base.OnActionExecuting(filterContext); } }
был открыт проект MVC 2, но оба метода по-прежнему применяются с незначительными изменениями.
Если вы хотите проверить время компиляции и intellisense для свойств в ваших представлениях, то ViewBag-это не путь.
рассмотрим класс BaseViewModel и ваши другие модели представления наследуются от этого класса, например:
Base ViewModel
public class BaseViewModel { public bool IsAdmin { get; set; } public BaseViewModel(IUserService userService) { IsAdmin = userService.IsAdmin; } }
просмотр конкретного ViewModel
public class WidgetViewModel : BaseViewModel { public string WidgetName { get; set;} }
Теперь код просмотра может получить доступ к свойству непосредственно в представлении
<p>Is Admin: @Model.IsAdmin</p>
вам не нужно возиться с действиями или изменять модель, просто используйте базовый контроллер и приведите существующий контроллер из макета viewcontext.
создайте базовый контроллер с требуемыми общими данными (заголовок/страница/местоположение и т. д.) и инициализацией действия...
public abstract class _BaseController:Controller { public Int32 MyCommonValue { get; private set; } protected override void OnActionExecuting(ActionExecutingContext filterContext) { MyCommonValue = 12345; base.OnActionExecuting(filterContext); } }
убедитесь, что каждый контроллер использует базовый контроллер...
public class UserController:_BaseController {...
приведите существующий базовый контроллер из контекста представления в вашем
_Layout.cshml
страница...@{ var myController = (_BaseController)ViewContext.Controller; }
теперь вы можете ссылаться на значения в вашем базовом контроллере со страницы макета.
@myController.MyCommonValue
Я нашел следующий подход, чтобы быть наиболее эффективным и дает отличный контроль, используя _ViewStart.файл chtml и условные операторы при необходимости:
_ 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<string>; }
Примечание:
PageData будет отлично работать в представлениях; однако, в случае a PartialView, его нужно будет передать из представления в маленький Частичный.