ユーザーが選択したアイテムを保持し、セッション変数に保存されるショッピング バスケットがあります。バスケットの状態、つまりbasket:1に関するすべてのビューにさまざまな値を表示したいのですが、これを単一のエントリポイントでビューに渡す方法しかわかりません。このリストですべてのビューを初期化するにはどうすればよいですか?
2 に答える
あなたの最善の策は、おそらくベース コントローラー、ベース ビュー モデル、インターフェイス、およびアクション フィルターの組み合わせです。
// Interface. To be implemented by model and controller.
public interface IHoldABasket
{
Basket Basket { get; set; };
}
// Base view model. Has a basket as public property.
public BaseBasketViewModel : IHoldABasket
{
public Basket Basket { get; set; }
}
// Base controller model. Also has a basket.
public BaseController : Controller, IHoldABasket
{
public Basket Basket { get; set; }
public BaseController()
{
AttemptBasketLoad();
}
private void AttemptBasketLoad()
{
// Replace the SomeMethodToLoadBasket with whatever method you use
// to retrieve a basket.
Basket = SomeMethodToLoadBasket();
}
}
// Action Filter
public class BasketAwareAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
// If controller can hold basket AND model can hold basket
if (filterContext.Controller is IHoldABasket
&& filterContext.Controller.ViewData.Model is IHoldABasket)
{
// Copy basket from controller into model.
// Will now be accessible through Basket property on model.
((IHoldABasket)filterContext.Controller.ViewData.Model)
.LoggedInUser
= ((IHoldABasket)filterContext.Controller).LoggedInUser;
}
base.OnActionExecuted(filterContext);
}
}
これでインフラストラクチャが整理されました。実際の例を見てみましょう。あなたはおそらく ProductListViewModel を持っています。これは、基本ビュー モデル クラスから継承する必要があります。
まず、ProductListViewModel が BaseBasketViewModel から継承されていることを確認してください。
public class ProductListViewModel : BaseBasketViewModel
{
}
継承のため、ビュー モデルにはバスケット オブジェクトが含まれており、IHoldABasket インターフェイスを実装しています。
コントローラーは BaseController から継承されます。
public class ProductController : BaseController
{
}
コントローラーメソッドは次のようになります。
[BasketAware]
public ViewResult Products(int page = 1)
{
// Load VM that implements IHoldABasket
// Really contrived, I know... :P
var vm = new ProductListViewModel() { Results = productServices.Search() };
return View(vm);
}
それだけです。ボンネットの下で起こっていることは
- ベースコントローラーはバスケットのロードを試み、見つかった場合は保管します
- コントローラーとモデルは共通のインターフェースを継承し、コントローラーからモデルへの自動コピーをより簡単に実現します。
- アクション フィルターは最後の最後に読み込まれます。コントローラーとモデルの両方がバスケットを保持できる場合 (例: 両方とも 器具
IHoldABasket
)、バスケットはコントローラーからモデルにコピーされます。 - BaseBasketViewModel から派生するすべてのビュー モデルには、Basket というパブリック プロパティがあります。
child action
レイアウトで をレンダリングできます。子アクションの考え方は、メイン アクションと並行していくつかのロジックを実行できるということです。
たとえば、次のコントローラーを使用できます。
public class ShoppingBasketInfoController: Controller
{
[ChildActionOnly]
public ActionResult Index()
{
var model = Session["info"] as ShoppingInfoViewModel;
return PartialView(model);
}
}
すると、対応する部分ビュー ( ~/Views/ShoppingBasketInfo/Index.cshtml
)が表示されます。
@model ShoppingInfoViewModel
<div>
You have @Html.DisplayFor(x => x.NbProducts) in your basket
</div>
そして、レイアウト内でこのアクションをレンダリングする適切な場所を見つけることができます。
@Html.Action("Index", "ShoppingBasketInfo")
これで、すべてのビューで、この情報がどこから来ているか、どのように保存されているか、どのビュー モデルを使用しているかを気にすることなく、指定された場所にこの情報が表示されます。主なアクションは完全に独立しています。
属性を使用して子アクションを装飾し[ChildActionOnly]
、たとえば を使用して、クライアントからの通常の HTTP リクエストを介してこのアクションにアクセスできないようにしました/ShoppingBasketInfo/Index
。メインの実行アクションのコンテキスト内でのみ使用できます。