9

カスタムモデルのバインド、モデルの状態、およびデータの注釈に関していくつか質問があります。

1)モデルにデータ注釈がある場合、カスタムモデルバインダーで検証を行うのは冗長ですか?それがデータ注釈のポイントであると私が思ったからです。

2)コントローラーがモデルの状態を有効でなくても有効として扱うのはなぜですか。主にNameプロパティをnullまたは短すぎます。

3)カスタムモデルバインダーをコンストラクターメソッドと考えても大丈夫ですか。それが私に思い出させてくれるからです。

まず、私のモデルです。

public class Projects
{     
    [Key]
    [Required]
    public Guid ProjectGuid { get; set; }

    [Required]
    public string AccountName { get; set; }

    [Required(ErrorMessage = "Project name required")]
    [StringLength(128, ErrorMessage = "Project name cannot exceed 128 characters")]
    [MinLength(3, ErrorMessage = "Project name must be at least 3 characters")]
    public string Name { get; set; }

    [Required]
    public long TotalTime { get; set; }
}

次に、カスタムモデルバインダーを使用して、モデルのいくつかのプロパティをバインドしています。それを機能させてからリファクタリングしようとするだけで、速くて汚いことを気にしないでください。

public class ProjectModelBinder : IModelBinder
{
     public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        if (controllerContext == null)
        {
            throw new ArgumentNullException("controllerContext");
        }
        if (bindingContext == null)
        {
            throw new ArgumentNullException("bindingContext");
        }
        var p = new Project();
        p.ProjectGuid = System.Guid.NewGuid();
        p.AccountName = controllerContext.HttpContext.User.Identity.Name;
        p.Name = controllerContext.HttpContext.Request.Form.Get("Name");
        p.TotalTime = 0;

        //
        // Is this redundant because of the data annotations?!?!
        //
        if (p.AccountName == null)
            bindingContext.ModelState.AddModelError("Name", "Name is required");

        if (p.AccountName.Length < 3)
            bindingContext.ModelState.AddModelError("Name", "Minimum length is 3 characters");

        if (p.AccountName.Length > 128)
            bindingContext.ModelState.AddModelError("Name", "Maximum length is 128 characters");

        return p;
    }
}

今私のコントローラーのアクション。

    [HttpPost]
    public ActionResult CreateProject([ModelBinder(typeof(ProjectModelBinder))]Project project)
    {
        //
        // For some reason the model state comes back as valid even when I force an error
        //
        if (!ModelState.IsValid)
            return Content(Boolean.FalseString);

        //_projectRepository.CreateProject(project);

        return Content(Boolean.TrueString);

    }

編集

別のstackoverflowの質問でいくつかのコードを見つけましたが、どの時点でこの可能なソリューションに次の値を挿入するかわかりません。

新しいオブジェクトが作成されたときに注入したいもの:

        var p = new Project();
        p.ProjectGuid = System.Guid.NewGuid();
        p.AccountName = controllerContext.HttpContext.User.Identity.Name;
        p.Name = controllerContext.HttpContext.Request.Form.Get("Name");
        p.TotalTime = 0;

上記のコードを以下のものにするにはどうすればよいですか(可能な解決策):

    public class ProjectModelBinder : DefaultModelBinder
    {
        public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            if (bindingContext.ModelType == typeof(Project))
            {
                ModelBindingContext newBindingContext = new ModelBindingContext()
                {

                    ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(
                        () => new Project(), // construct a Project object,
                        typeof(Project)         // using the Project metadata
                    ),
                    ModelState = bindingContext.ModelState,
                    ValueProvider = bindingContext.ValueProvider

                };

                // call the default model binder this new binding context
                return base.BindModel(controllerContext, newBindingContext);
            }
            else
            {
                return base.BindModel(controllerContext, bindingContext);
            }
        }

    }

}
4

1 に答える 1

10

DefaultModelBinderから継承し、メソッドをオーバーライドし、BindModelメソッドを呼び出してbase.BindModelから手動で変更(GUID、アカウント名、合計時間を設定)すると、作業がはるかに簡単になります。

1)実行したとおりに検証することは冗長です。デフォルトのように検証メタデータを反映するコードを記述したり、モデルバインダーで使用していないため、データアノテーションの検証を削除したりできます。

2)わかりません。正しいようです。コードをステップ実行して、カスタムバインダーが該当するすべてのルールに入力されていることを確認する必要があります。

3)確かにファクトリーですが、コンストラクターではありません。


編集:ソリューションに近づくことはできませんでした。モデルファクトリ関数で必要なプロパティを設定するだけです。

public class ProjectModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        if (bindingContext.ModelType == typeof(Project))
        {
            ModelBindingContext newBindingContext = new ModelBindingContext()
            {

                ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(
                    () => new Project()  // construct a Project object
                    {
                        ProjectGuid = System.Guid.NewGuid(),
                        AccountName = controllerContext.HttpContext.User.Identity.Name,
                        // don't set name, thats the default binder's job
                        TotalTime = 0,
                    }, 
                    typeof(Project)         // using the Project metadata
                ),
                ModelState = bindingContext.ModelState,
                ValueProvider = bindingContext.ValueProvider

            };

            // call the default model binder this new binding context
            return base.BindModel(controllerContext, newBindingContext);
        }
        else
        {
            return base.BindModel(controllerContext, bindingContext);
        }
    }

}

CreateModelまたは、メソッドをオーバーライドすることもできます。

protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, System.Type modelType)
{
    if (modelType == typeof(Project))
    {
        Project model = new Project()
        {
            ProjectGuid = System.Guid.NewGuid(),
            AccountName = controllerContext.HttpContext.User.Identity.Name,
            // don't set name, thats the default binder's job
            TotalTime = 0,
        };

        return model;
    }

    throw new NotSupportedException("You can only use the ProjectModelBinder on parameters of type Project.");
}
于 2011-04-28T15:21:06.867 に答える