7

ビュー モデルが作成されると、オプション (ドロップダウン リストで使用されるものなど) をビュー モデルのセッター プロパティに設定できます。問題は、そのビュー モデルが後でパラメーターとして (フレームワークによって!) アクション メソッドに渡されるときに、それらのプロパティ値が自動的に再入力されないことです。そのため、検証エラーのためにフォームを再表示する必要がある場合は、これらのオプションを再度入力します。

この質問で私が具体的に求めている1つの潜在的な解決策は、MVCフレームワークがコンストラクター注入を使用してビューモデルをインスタンス化する方法です。これにより、ビューモデルコンストラクターにある種のデータアクセスオブジェクト(リポジトリなど)の実装が提供されます) ビューによって要求されたときにオプションを取得するために使用できる (たとえば、ヘルパー メソッド "DropDownListFor" で)。

解決策は IModelBinderProvider または IModelBinder の実装と関係があるかもしれないと思いますが、ネット上のあちこちのサンプルコードスニペットからこれらのことを試した後、私はまだ完全に機能する例を探しています。すべてのものをまとめる方法の一部。

「依存性注入」の代わりに「依存性ルックアップ」など、選択リストを作成する方法についての別の議論を探している場合は、次の議論を確認することをお勧めします 。 GET/POST で ViewModel の SelectList を設定する方法

数日前、このスレッドで現在探している「依存性注入」について、次のフォローアップ質問をそのスレッドに書きました: https://stackoverflow.com/a/8674525/310457 (これは、私が解決策を探している問題)

しかし、誰かが特定性の低いタイトルの古いスレッドを見つけてくれることを期待する代わりに、私が探しているものについてより具体的な件名でこの新しい質問を作成しました. また、私が探しているこの特定のソリューションに関してフォローアップしたい人のために、そのスレッドからこの新しい質問へのリンクも提供します。

4

1 に答える 1

10

ViewModels に、Constructor を介して何かを自動的に注入する必要があると想定しています。たとえば、View が何を表示するかを決定するために使用するある種の構成オブジェクトです。また、MVC がコントローラー アクションの引数からモデル インスタンスを自動的に作成してバインドしようとすると、このアプローチが「このオブジェクトにパラメーターなしのコンストラクターが定義されていません」というエラーを引き起こしていると想定しています。また、DI フレームワークを使用して、SiteConfig オブジェクトを実行時にコントローラーに自動的に挿入するとします。

これは、解決しなければならない唯一の問題は、注入されたオブジェクトを Controller からアクションの ViewModel に自動的にバインドするときに取得する方法だけであることを意味します。

それでは、他のモデルが継承する基本モデルを定義しましょう。

BaseViewModel

public class BaseViewModel
{
    public ISiteConfig SiteConfig { get; set; }

    public BaseViewModel(ISiteConfig siteConfig)
    {
        this.SiteConfig = siteConfig;
    }
}

そして、それを継承するモデルを作成しましょう。

IndexViewModel

public class IndexViewModel : BaseViewModel
{
    public string SomeIndexProperty { get; set; }

    public IndexViewModel (ISiteConfig siteConfig) : base(siteConfig) {}
}

次に、コントローラが継承するベース コントローラを定義しましょう。

BaseController

public abstract class BaseController : Controller
{
    protected BaseController(ISiteConfig siteConfig)
    {
        _siteConfig = siteConfig;
    }

    private readonly ISiteConfig _siteConfig;

    public ISiteConfig SiteConfig
    {
        get
        {
            return _siteConfig;
        }
    }
}

次に、実際のコントローラーを定義します。

ホームコントローラー

public HomeController: BaseController
{
    public HomeController(ISiteConfig siteConfig): base(siteConfig) {}
}

DI に Ninject を使用していると仮定すると、Ninject は自動的にコントローラーを作成し、ISiteConfig実行時に具体的なオブジェクトをそのコンストラクターに渡すように構成されます。

ここで、Action を Controller に追加します。

インデックス アクション

public ActionResult Index(IndexViewModel model)
{
    return View(model);
}

MVC は引数を取らない ViewModel コンストラクターを見つけることができないため、インデックス アクションを呼び出そうとすると、他に何もしなくても MVC は「パラメーターなしのコンストラクター」エラーで爆発します。

そして、答え。デフォルトの ModelBinder をオーバーライドする必要があります。

BaseViewModelBinder

public class BaseViewModelBinder : DefaultModelBinder
{
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        if (modelType == typeof(BaseViewModel) || modelType.IsSubclassOf(typeof(BaseViewModel)))
        {
            var baseControl = controllerContext.Controller as BaseController;
            if (baseControl == null)
            {
                throw new Exception("The Controller must derive from BaseController");
            }

            var instance = Activator.CreateInstance(modelType, baseControl.SiteConfig);
            bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => instance, modelType);
            return instance;
        }
        else
        {
            return base.CreateModel(controllerContext, bindingContext, modelType);
        }
    }
}

そして、これをデフォルトのモデル バインダーとして設定する必要がありますglobal.asax.cs

protected void Application_Start()
{
    ...
    ModelBinders.Binders.DefaultBinder = new BaseViewModelBinder();
}

それで全部です。ご覧のとおり、インデックス アクションを表示すると、MVC はカスタム モデル バインダーを使用します。IndexViewModel が BaseViewModel から派生していることを認識し、アクションのコントローラーで見つけることができる ISiteConfig を使用して IndexViewModel インスタンスをスピンアップしようとします (コントローラーは BaseController から派生するため)。

于 2014-06-11T15:10:03.193 に答える