program tip

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

radiobox 2020. 8. 31. 07:37
반응형

컨트롤러에 대한 기본 클래스를 사용하지 않고 모든 뷰에 대한 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를 사용하고 전역에 사용자 지정 클래스를 등록하는 것입니다. 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()

Since ViewBag properties are, by definition, tied to the view presentation and any light view logic that may be necessary, I'd create a base WebViewPage and set the properties on page initialization. It's very similar to the concept of a base controller for repeated logic and common functionality, but for your views:

    public abstract class ApplicationViewPage<T> : WebViewPage<T>
    {
        protected override void InitializePage()
        {
            SetViewBagDefaultProperties();
            base.InitializePage();
        }

        private void SetViewBagDefaultProperties()
        {
            ViewBag.GlobalProperty = "MyValue";
        }
    }

And then in \Views\Web.config, set the pageBaseType property:

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

Brandon's post is right on the money. As a matter of fact, I would take this a step further and say that you should just add your common objects as properties of the base WebViewPage so you don't have to cast items from the ViewBag in every single View. I do my CurrentUser setup this way.


You could use a custom ActionResult:

public class  GlobalView : ActionResult 
{
    public override void ExecuteResult(ControllerContext context)
    {
        context.Controller.ViewData["Global"] = "global";
    }
}

Or even a ActionFilter:

public class  GlobalView : ActionFilterAttribute 
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        filterContext.Result = new ViewResult() {ViewData = new ViewDataDictionary()};

        base.OnActionExecuting(filterContext);
    }
}

Had an MVC 2 project open but both techniques still apply with minor changes.


You don't have to mess with actions or change the model, just use a base controller and cast the existing controller from the layout viewcontext.

Create a base controller with the desired common data (title/page/location etc) and action initialization...

public abstract class _BaseController:Controller {
    public Int32 MyCommonValue { get; private set; }

    protected override void OnActionExecuting(ActionExecutingContext filterContext) {

        MyCommonValue = 12345;

        base.OnActionExecuting(filterContext);
    }
}

Make sure every controller uses the base controller...

public class UserController:_BaseController {...

Cast the existing base controller from the view context in your _Layout.cshml page...

@{
    var myController = (_BaseController)ViewContext.Controller;
}

Now you can refer to values in your base controller from your layout page.

@myController.MyCommonValue

If you want compile time checking and intellisense for the properties in your views then the ViewBag isn't the way to go.

Consider a BaseViewModel class and have your other view models inherit from this class, eg:

Base ViewModel

public class BaseViewModel
{
    public bool IsAdmin { get; set; }

    public BaseViewModel(IUserService userService)
    {
        IsAdmin = userService.IsAdmin;
    }
}

View specific ViewModel

public class WidgetViewModel : BaseViewModel
{
    public string WidgetName { get; set;}
}

Now view code can access the property directly in the view

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

I have found the following approach to be the most efficient and gives excellent control utilizing the _ViewStart.chtml file and conditional statements when necessary:

_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>;
 }

Note:

PageData will work perfectly in Views; however, in the case of a PartialView, it will need to be passed from the View to the child Partial.

참고URL : https://stackoverflow.com/questions/5453327/how-to-set-viewbag-properties-for-all-views-without-using-a-base-class-for-contr

반응형