과거에는 모든 컨트롤러가 공통 기본 컨트롤러에서 상속되도록하여 현재 사용자와 같은 공통 속성을 ViewData / ViewBag에 전역 방식으로 고정했습니다.
이를 통해 기본 컨트롤러에서 IoC를 사용할 수 있었으며 이러한 데이터에 대해 글로벌 공유에 접근 할 수 없었습니다.
이런 종류의 코드를 MVC 파이프 라인에 삽입하는 다른 방법이 있는지 궁금합니다.
과거에는 모든 컨트롤러가 공통 기본 컨트롤러에서 상속되도록하여 현재 사용자와 같은 공통 속성을 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>();
});
Resolve
부분은 e.Context.Resolve
무엇입니까? 나는 ... 나는 Ninject에 사용 해요 언급해야한다
가장 좋은 방법은 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()
MembershipService
하시겠습니까?
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.Title
속성을 설정하여 처리되고 공유 레이아웃에서 유일한 것은 <title>@ViewBag.Title</title>
. 각보기가 별개이고 기본보기 페이지는 모든보기에서 진정으로 공통된 데이터를위한 것이기 때문에 기본 애플리케이션보기 페이지와 같은 것에 실제로 적합하지 않습니다.
브랜든의 게시물이 바로 돈입니다. 사실, 나는 이것을 한 단계 더 나아가서 기본 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?)
사용자 정의 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>
다음 접근 방식이 가장 효율적이며 필요한 경우 _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로 전달되어야합니다.