代码之家  ›  专栏  ›  技术社区  ›  Tr1stan

MVC UpdateModel和子类与基类

  •  4
  • Tr1stan  · 技术社区  · 15 年前

    我希望对运行时检索的子类使用UpdateModel方法,如果有人能告诉我是否正在对它进行一个总的散列和/或我试图做的事情是否可能的话,那将是非常好的。

    我使用一个通用操作来控制一堆部分视图的验证;我试图避免每个部分视图都有一个特定的操作。

    每个局部视图都有一个从基础模型派生的唯一模型:

    public class ModelA : ModelBase{
         [Required]
         public string SomeStringProperty{get;set;}
    ...
    }
    public class ModelB : ModelBase{
         [Required]
         public DateTime? SomeDateProperty{get;set;}
    ...
    }
    public class ModelBase{
         public Guid InstanceId{get;set;}
    }
    

    我正在对操作使用FormCollection来获取提交的表单元素及其值,这包括视图应用于验证其请求的模型类型。 忽略这个例子中的安全含义,我知道它们,这是一个内部唯一的概念证明

        [HttpPost]
        public ActionResult ChangeCaseState(int id, FormCollection formCollection)
        {
            Guid instanceId = new Guid(formCollection["instanceId"]);
            string modelType = formCollection["modelType"];
            //Return a specific Model class based on the event/modelType
            var args = GetStateModelClass(modelType, instanceId);
    
            try
            {
                UpdateModel(args);
                if(Model.IsValid){
                 ...
            }
            catch (Exception)
            {
                return View("~/Views/Shared/StateForms/" + modelType + ".ascx", args);
            }...
    

    下面是根据传递给控制器的modelType返回子类的代码。

    private static ModelBase StateModelClassFactory(string stateModelTypeName, Guid instanceId)
            {
                switch (stateModelTypeName)
                {
                    case "modelTypeA":
                        return new ModelA(workflowInstanceId);
                    case "modelTypeB":
                        return new ModelB(workflowInstanceId);
        ...
        }
    

    因为StateModelClassFactory方法的返回类型是基类,所以即使我实际上返回的是子类,UpdateModel方法使用的模型绑定器也只绑定基类中的值。

    我能解决这个问题吗?

    更新:

    我创建了一个客户模型活页夹:

    public class CustomModelBinder : DefaultModelBinder
        {
          public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
                {
    

    并将新的模型绑定器分配给正确的基类,以查看引擎盖下面发生了什么:

    ModelBinders.Binders.Add(typeof(ModelBase), new CaseController.CustomModelBinder());
    

    当我调试模型绑定器并检查bindingContext时,model属性表示正确的子类,但ModelType属性是基类的。我是否应该考虑在BindModel方法中更改ModelType?如果有任何关于如何做到这一点的指针,那么ModelType上的setter似乎是多余的。我还注意到,子类中的SomeDateProperty实际上位于PropertyMetadata属性中……似乎非常接近于我想要的行为。

    3 回复  |  直到 15 年前
        1
  •  5
  •   DMac the Destroyer    15 年前

    我刚刚遇到了这个特殊的问题,发现一个更好的通用方法是将您的模型 dynamic 当它进入 UpdateModel :

    [HttpPost]
    public ActionResult ChangeCaseState(int id, FormCollection formCollection)
    {
        ...try
        {
            UpdateModel((dynamic)args);//!!notice cast to dynamic here
            if(Model.IsValid){
             ...
        }
        catch...
    

    这似乎设置了我的类型的所有可用属性,而不管我的变量是否与基类型进行了delcare。

    在CodePlex中有一个针对此问题的工作项: http://aspnet.codeplex.com/workitem/8277?ProjectName=aspnet

        2
  •  2
  •   Tr1stan    15 年前

    所以我想我已经解决了我的问题。基本上是因为我在调用UpdateModel之前检索模型类的方式,模型绑定器绑定基类,即使模型是子类的模型-这是我用来解决我的特殊问题的代码:

      public class SubClassModelBinder : DefaultModelBinder
    {
        public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            var model = bindingContext.Model;
            var metaDataType = ModelMetadataProviders.Current.GetMetadataForType(null, model.GetType());
            bindingContext.ModelMetadata = metaDataType;
            bindingContext.ModelMetadata.Model = model;
    
            return base.BindModel(controllerContext, bindingContext);
        }
    }
    

    在Global.asax中:

    ModelBinders.Binders.Add(typeof(ModelBase), new SubClassModelBinder ());
    

    多亏达林的第一个投球。

        3
  •  0
  •   Darin Dimitrov    15 年前

    要解决此问题,可以为基类型编写自定义模型绑定器,该绑定器基于string属性的值将返回正确的子实例。

    推荐文章