Как установить свойства ViewBag для всех видов без использования базового класса для контроллеров? - PullRequest
90 голосов
/ 28 марта 2011

В прошлом я вставлял общие свойства, такие как текущий пользователь, в ViewData / ViewBag в глобальном стиле, поскольку все контроллеры наследуются от общего базового контроллера.

Это позволило мне использовать IoCна базовом контроллере, а не просто обращаться к глобальному общему ресурсу для таких данных.

Мне интересно, есть ли альтернативный способ вставки такого рода кода в конвейер MVC?

Ответы [ 8 ]

236 голосов
/ 15 января 2014

Лучший способ - использовать 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()
39 голосов
/ 28 марта 2011

Поскольку свойства 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>
22 голосов
/ 28 марта 2011

Не пробовал, но вы можете посмотреть , зарегистрировать свои просмотры и затем установить данные в процессе активации.

Поскольку представления регистрируются «на лету», синтаксис регистрации не помогает подключиться к событию 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.

Редактировать: Альтернативный подход с меньшим количеством кода - просто подключите к контроллеру

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>();
            };
        }
    }
}

Редактировать 2: Другой подход, который работает непосредственно из регистрационного кода контроллера:

builder.RegisterControllers(asm)
    .OnActivated(e => {
        dynamic viewBag = ((Controller)e.Instance).ViewBag;
        viewBag.Config = e.Context.Resolve<Config>();
        viewBag.Identity = e.Context.Resolve<IIdentity>();
    });
17 голосов
/ 07 апреля 2011

Пост Брэндона прямо на деньги. На самом деле, я бы сделал еще один шаг вперед и сказал бы, что вы просто должны добавить свои общие объекты как свойства базы WebViewPage , чтобы вам не приходилось приводить элементы из ViewBag в каждом отдельном представлении. Я выполняю настройку CurrentUser таким образом.

8 голосов
/ 28 марта 2011

Вы можете использовать собственный 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, но оба метода все еще применяются с незначительными изменениями.

3 голосов
/ 25 июля 2017

Вам не нужно связываться с действиями или менять модель, просто используйте базовый контроллер и приведите существующий контроллер из контекста макета.

Создать базовый контроллер с желаемыми общими данными (заголовок / страница / местоположение и т. Д.) И инициализацией действия ...

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
3 голосов
/ 22 января 2015

Если вы хотите проверить время компиляции и 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>
2 голосов
/ 29 августа 2017

Я считаю следующий подход наиболее эффективным и дает превосходный контроль, используя файл _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 его необходимо будет передать из View в дочерний Partial.

...