Как установить свойства 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 и регистрация пользовательского класса в глобальном. эйсакс (событий 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;
}
}
}
зарегистрируйте свой пользовательский класс в глобальном. эйсакс (событий 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>
сообщение Брэндона прямо на деньги. На самом деле, я бы сделал этот шаг дальше и сказал, что вы должны просто добавить свои общие объекты как свойства на база 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 и ваши другие модели представления наследуются от этого класса, например:
Базовая 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;}
}
Теперь код просмотра может получить доступ к свойству непосредственно в view
<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 будет отлично работать в представлениях; однако, в случае PartialView, он должен быть передан из представления в маленький Частичный.