컨트롤러에 대한 기본 클래스를 사용하지 않고 모든 뷰에 대한 ViewBag 속성을 설정하는 방법은 무엇입니까?


96

과거에는 모든 컨트롤러가 공통 기본 컨트롤러에서 상속되도록하여 현재 사용자와 같은 공통 속성을 ViewData / ViewBag에 전역 방식으로 고정했습니다.

이를 통해 기본 컨트롤러에서 IoC를 사용할 수 있었으며 이러한 데이터에 대해 글로벌 공유에 접근 할 수 없었습니다.

이런 종류의 코드를 MVC 파이프 라인에 삽입하는 다른 방법이 있는지 궁금합니다.

답변:


22

내가 시도하지 않았지만 등록한 다음 활성화 프로세스 중에 뷰 데이터를 설정하는 것을 볼 수 있습니다.

뷰는 즉석에서 등록되기 때문에 등록 구문은 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>();
    });

정확히 내가 필요한 것. 즉시 작동하도록 답변 업데이트
Scott Weinstein 2011

훌륭한 기능-귀하의 접근 방식에 따라 이번에는 모듈이 필요없는 또 다른 단순화를 추가했습니다.
Nicholas Blumhardt 2011

Resolve부분은 e.Context.Resolve무엇입니까? 나는 ... 나는 Ninject에 사용 해요 언급해야한다
drzaus

244

가장 좋은 방법은 ActionFilterAttribute를 사용하는 것입니다. .Net Core 및 .Net Framework에서 사용하는 방법을 보여 드리겠습니다.

.Net Core 2.1 및 3.1

public class ViewBagActionFilter : ActionFilterAttribute
{
    public override void OnResultExecuting(ResultExecutingContext context)
    {
        // for razor pages
        if (context.Controller is PageModel)
        {
            var controller = context.Controller as PageModel;
            controller.ViewData.Add("Avatar", $"~/avatar/empty.png");
            // or
            controller.ViewBag.Avatar = $"~/avatar/empty.png";

            //also you have access to the httpcontext & route in controller.HttpContext & controller.RouteData
        }

        // for Razor Views
        if (context.Controller is Controller)
        {
            var controller = context.Controller as Controller;
            controller.ViewData.Add("Avatar", $"~/avatar/empty.png");
            // or
            controller.ViewBag.Avatar = $"~/avatar/empty.png";

            //also you have access to the httpcontext & route in controller.HttpContext & controller.RouteData
        }

        base.OnResultExecuting(context);
    }
}

그런 다음 startup.cs에 등록해야합니다.

.Net Core 3.1

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(options => { options.Filters.Add(new Components.ViewBagActionFilter()); });
}

.Net Core 2.1

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options =>
        {
            options.Filters.Add(new Configs.ViewBagActionFilter());
        });
}

그런 다음 모든보기 및 페이지에서 사용할 수 있습니다.

@ViewData["Avatar"]
@ViewBag.Avatar

.Net Framework (ASP.NET MVC .Net Framework)

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

9
왜 이것이 더 많이 찬성되지 않았는지 이해할 수 없습니다. 그것은 다른 사람보다 훨씬 덜 침습적 접근 방식
joshcomley

5
이것을 찾기위한 8 시간의 조사 ... 완벽한 답. 정말 고맙습니다.
deltree 2014 년

3
+1 글로벌 데이터를 통합하는 멋지고 깨끗한 방법. 이 기술을 사용하여 모든 페이지에 내 사이트 버전을 등록했습니다.
Will Bickford 2014-08-14

4
훌륭하고 쉽고 눈에 띄지 않는 솔루션입니다.
Eugen Timm

3
하지만 IoC는 어디에 있습니까? 즉, 어떻게 전환 MembershipService하시겠습니까?
drzaus 2014

39

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>

이 설정의 문제는 한보기에서 ViewBag의 속성에 값을 설정 한 다음 다른보기 (예 : 공유 _Layout보기)에서 액세스하려고하면 첫 번째보기에 설정된 값 값이 레이아웃보기에서 손실되었습니다.
Pedro

@Pedro는 확실히 사실이지만 ViewBag는 응용 프로그램에서 지속적인 상태 소스가 아니라고 주장합니다. 해당 데이터를 세션 상태로 설정하고 기본 뷰 페이지에서 꺼내서 존재하는 경우 ViewBag에 설정할 수 있습니다.
Brandon Linton 2012 년

유효한 포인트가 있지만 거의 모든 사람들이 다른 뷰의 한 뷰에서 데이터 세트를 사용합니다. 한보기에서 페이지 제목을 설정하고 공유 레이아웃보기를 설정 한 다음 html 문서의 <title> 태그에 인쇄하는 것과 같습니다. "자식"보기에서 "ViewBag.DataTablesJs"와 같은 부울을 설정하여 "마스터"레이아웃보기에 html의 머리에 적절한 JS 참조를 포함하도록 설정하여이 작업을 한 단계 더 나아가고 싶습니다. 레이아웃과 관련된 것이라면 괜찮다고 생각합니다.
페드로

@Pedro는 제목 태그 상황에서 잘 맞습니다. 일반적으로 각 뷰가 ViewBag.Title속성을 설정하여 처리되고 공유 레이아웃에서 유일한 것은 <title>@ViewBag.Title</title>. 각보기가 별개이고 기본보기 페이지는 모든보기에서 진정으로 공통된 데이터를위한 것이기 때문에 기본 애플리케이션보기 페이지와 같은 것에 실제로 적합하지 않습니다.
Brandon Linton 2012 년

@Pedro 나는 당신이 말하는 것을 이해하고 Brandon이 요점을 놓친 것 같습니다. 사용자 지정 WebViewPage를 사용하고 있었고 사용자 지정 WebViewPage의 사용자 지정 속성을 사용하여보기 중 하나에서 레이아웃보기로 일부 데이터를 전달하려고했습니다. 뷰에서 속성을 설정하면 사용자 지정 WebViewPage에서 ViewData가 업데이트되지만 레이아웃 뷰에 도달하면 ViewData 항목이 이미 손실되었습니다. 사용자 지정 WebViewPage에서 ViewContext.Controller.ViewData [ "SomeValue"]를 사용하여 문제를 해결했습니다. 누군가에게 도움이되기를 바랍니다.
Imran Rashid

17

브랜든의 게시물이 바로 돈입니다. 사실, 나는 이것을 한 단계 더 나아가서 기본 WebViewPage의 속성 으로 공통 개체를 추가해야 하므로 모든 단일 뷰에서 ViewBag의 항목을 캐스팅 할 필요가 없다고 말할 것입니다. 이 방식으로 CurrentUser 설정을 수행합니다.


나는 오류와 함께 작업이 가져올 수 없습니다'ASP._Page_Views_Shared__Layout_cshtml' does not contain a definition for 'MyProp' and no extension method 'MyProp' accepting a first argument of type 'ASP._Page_Views_Shared__Layout_cshtml' could be found (are you missing a using directive or an assembly reference?)
Sprintstar

+1, 이것은 모든 뷰에서 전역 적으로 사용할 수 있어야하는 비 정적 유틸리티 클래스의 인스턴스를 공유하기 위해 제가하는 일입니다.
Nick Coad

9

사용자 정의 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 프로젝트가 열려 있었지만 두 기술 모두 약간의 변경 사항이 적용됩니다.


5

액션을 엉망으로 만들거나 모델을 변경할 필요가 없습니다. 기본 컨트롤러를 사용하고 레이아웃 뷰 컨텍스트에서 기존 컨트롤러를 캐스팅하면됩니다.

원하는 공통 데이터 (제목 / 페이지 / 위치 등) 및 작업 초기화로 기본 컨트롤러를 만듭니다.

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

뷰의 속성에 대한 컴파일 시간 검사 및 인텔리 젠스를 원한다면 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;}
}

이제보기 코드가보기에서 직접 속성에 액세스 할 수 있습니다.

<p>Is Admin: @Model.IsAdmin</p>

2

다음 접근 방식이 가장 효율적이며 필요한 경우 _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로 전달되어야합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.