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

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

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

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



답변

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

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


답변

가장 좋은 방법은 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()


답변

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


답변

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

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

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


답변

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