MVC UpdateModel 和子类与基类

发布于 2024-10-04 18:43:48 字数 2367 浏览 10 评论 0原文

我正在寻找对在运行时检索的子类使用 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());

当我调试模型绑定器并检查绑定上下文时,Model 属性代表正确的子类,但 ModelType 属性是基类的属性。我应该考虑更改 BindModel 方法中的 ModelType 吗?如果有关于如何执行此操作的任何指示,则 ModelType 上的设置器似乎已变得多余。我还注意到子类中的 SomeDateProperty 实际上位于 PropertyMetadata 属性中......看起来非常接近我想要的行为。

I'm looking to use the UpdateModel method to a Sub Class that retrieved at runtime, would be great if someone could shed the light on whether I'm making a total hash of it and/or whether or not what I'm trying to do is possible.

I'm using a generic action to control the validation of a bunch of partial views; I'm trying to get away from having a specific action per partial view.

Each partial view has a unique Model that derives from a Base Model:

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

I'm using the FormCollection on the Action to get the submitted form elements and their values, this includes the type of model that the View should be using to validate its request. Ignore the security implications of this for this example, I'm aware of them and this is an internal only proof of concept

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

And here is the code I'm using to return a Sub Class based on the modelType passed to the controller.

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

Because the return type of the StateModelClassFactory method is of the Base Class, even though I'm actually returning a Sub Class, the Model Binder used by the UpdateModel method only binds against the values within the Base Class.

Any ideas on how I can solve this problem?

UPDATE:

I created a Customer Model Binder:

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

And Assigned the new Model Binder to the correct Base Class to see what was going on a little more under the hood:

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

When I debug the model binder and inspect the bindingContext, the Model property represets the correct Sub Class, but the ModelType property is that of the Base Class. Should I be looking at changing the ModelType within the BindModel method? If so any pointers on how to do this, the setter on the ModelType seems to have been made redundant. I also noticed that the SomeDateProperty from the Sub Class is actaully in the PropertyMetadata property....Seems so close to behaving as I'd like.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

坏尐絯℡ 2024-10-11 18:43:48

我刚刚遇到这个特定问题,发现更好的通用方法是将模型转换为动态模型,同时将其传递给 UpdateModel:

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

这似乎设置了所有可用属性我的类型,无论我的变量是否用基本类型声明。

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

I just ran into this particular issue and found that a better general approach would be just to cast your model to dynamic while passing it into UpdateModel:

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

This appears to set all available properties of my type, regardless of whether my variable is delcared with the base type.

There's a work item filed in CodePlex for this issue: http://aspnet.codeplex.com/workitem/8277?ProjectName=aspnet

£烟消云散 2024-10-11 18:43:48

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

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

感谢 Darin 最初的指点。

So I think I've solved my problem. Basically because of the way that I'm retrieving the Model class before calling the UpdateModel, the Model Binder is binding the BaseClass even though the Model was that of the SubClass - this is the code I used to solve my particular problem:

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

And in the Global.asax:

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

Thanks to Darin for his inital pointer.

呆头 2024-10-11 18:43:48

为了解决这个问题,您可以为基本类型编写一个自定义模型绑定器,它基于字符串属性的值将返回正确的子实例。

To solve this problem you could write a custom model binder for the base type which based on the value of the string property will return the correct child instance.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文