過去に、現在のユーザーなどの共通のプロパティを、すべてのコントローラーに共通のベースコントローラーから継承させることにより、グローバルな方法で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";
};
}
}
}
これは、私からの「ツールはハンマーだ」というタイプの提案の1つかもしれません。より簡単な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()
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クラスを検討し、他のビューモデルにこのクラスを継承させます。例:
ベースビューモデル
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に渡す必要があります。