Asp.net-Mvc

自定義模型綁定、模型狀態和數據註釋

  • April 28, 2011

我有一些關於自定義模型綁定、模型狀態和數據註釋的問題。

1)如果我的模型上有數據註釋,在自定義模型綁定器中進行驗證是否多餘,因為這就是我認為數據註釋的意義所在。

2)為什麼我的控制器將模型狀態視為有效,即使它不是,主要是我將 Name 屬性設置為 null 或太短。

  1. 是否可以將自定義模型綁定器視為構造方法,因為它們讓我想起了這一點。

首先是我的模型。

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);
           }
       }

   }

}

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.");
}

引用自:https://stackoverflow.com/questions/5820637