以前は、すべてのコントローラーに共通の基本コントローラーを継承させることにより、現在のユーザーなどの共通のプロパティをグローバルな方法で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";
};
}
}
}
これは私からの「唯一の道具はハンマー」タイプの提案の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>();
});
Resolve
一部はe.Context.Resolve
何ですか?私はNinjectに慣れていることに言及する必要があります...
最善の方法は、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()
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クラスを検討し、他のビューモデルにこのクラスを継承させます。例:
ベースビューモデル
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の場合は、ビューから子Partialに渡す必要があります。