即使属性已设置且有效,为什么 TryUpdateModel 仍返回 false?

发布于 2025-01-05 18:05:43 字数 3747 浏览 3 评论 0原文

我有基于 ASP.Net MVC3 的 Web 应用程序。我需要一个“创建”视图,在用户从下拉列表中选择要创建的子类型之前,该视图不会知道模型类型。为了尝试解决此问题,我在 Shared/EditorTemplates 下为每个派生模型类型创建了一个编辑器模板。这允许我创建一个对视图模型强类型的“Create.cs”。视图模型只有两个成员,一个枚举和一个复杂类型。这个想法是,视图最初仅显示一个下拉列表(枚举成员的编辑器),然后当用户最初提交指定的“模型类型”(下拉选择的值)时,POST 操作可以检查“模型类型” " 为视图模型的单个复杂成员指定并实例化正确的派生模型类型,该成员的类型是所有可能的“模型类型”的基本类型。

抽象+派生类型模型对象...

public abstract class MyModelBase
{
    public MyModelType_e ModelType {get; set; }
    [Required]
    public string Name { get; set; }
}

public class DerivedType1 : MyModelBase
{ ... }

public class DerivedType2 : MyModelBase
{ ... }

public class DerivedType3 : MyModelBase
{ ... }

我有一个复杂的视图模型,如下...

public enum MyModelType_e
{
    DerivedType1 = 0,
    DerivedType2 = 1,
    DerivedType3 = 2
}

public class MyModelCreate
{
    public MyModelType_e ModelTypeForSelectList { get; set; }
    public MyModelBase ModelBase { get; set; }
}

我的 GET 控制器操作实例化了视图的上述视图模型,即,仅显示一个下拉列表,其中包含基于 MyModelType_e 枚举的项目 +模型的“ModelBase”属性的值最初为 null。因此 GET 操作方法看起来像这样...

[HttpGet]
public ActionResult Create()
{
    return View(new MyModelCreate());
}

请注意下面大写字母中关于我的问题的关键的注释,即 TryUpdateModel 失败(见下文),即使它将 ModelBase(派生类型)成员的属性设置为相应的按预期形成值...

[HttpPost]
public ActionResult Create(MyModelCreate model)
{
    if (model.ModelBase == null || 
        (int)model.ModelTypeForSelectList != model.ModelBase.ModelType)
    {
        switch (model.ModelType)
        {
            case MyModelType_e.DerivedType1:
                model.ModelBase = new DerivedType1();
                break;
            case MyModelType_e.DerivedType2:
                model.ModelBase = new DerivedType2();
                break;
            case MyModelType_e.DerivedType3:
                model.ModelBase = new DerivedType3();
                break;
        }
        return View(model);
    }

    if (!TryUpdateModel(model.ModelBase))
        return View(model); // <<< THIS HAPPENS EVEN THOUGH ModelBase APPEARS TO BE UPDATED PROPERLY... 
    // For instance, I can see right here with intellisense that model.ModelBase.Name
    // is NOT null or empty but rather is truly updated with the correct form value(s)...

    // TODO: Insert the record, etc... (currently we never get here...)
}

因此上面的部分是问题的根源,但这是我的观点以帮助理解...

 @model MyNamespace.MyModelCreate

 <h2>Create</h2>

 ...

 @using (Html.BeginForm())
 {
     @Html.ValidationSummary(false)
    <fieldset>
    <legend>Input</legend>
    <div class="editor-label">
    @Html.Label("Select Model Type")
    </div>
    <div>
    @Html.EnumDropDownListFor(model => model.ModelType)
    @Html.ValidationMessageFor(model => model.ModelType)
    </div>

    @* 
    Conditionally show the correct editor template... 
    There is one existing under ../Shared/EditorTemplates for each
    derived type (DerivedType1, DerivedType2, DerivedType3, etc...)
    This much is working in the sense that the correct editor fields
    are displayed based on what the user selects in the above drop-down.
    *@
    @if (Model.InputModel != null)
    {  
        @Html.EditorFor(model => model.ModelBase);
    }

    <p>
        <input type="submit" value="Continue" />
    </p>              
    </fieldset>
}

因此,一旦使用初始 POST(选择模型类型),我的 POST 操作方法就会落入 TryUpdateModel按照设计,但由于某种原因验证失败了。我真正不明白的部分是,验证摘要报告“需要名称”,即使我可以清楚地看到 TryUpdateModel 在视图模型的 ModelBase 成员上正确设置了名称属性。

我非常感谢这里的任何帮助或指导...我对 ASP.NET MVC 相当陌生,并且我愿意以不同的方式做事,因为我知道可能还有其他方法可以设计我的请求/操作 + 视图来完成此任务“多步骤”问题,但我真的只是追求最简单的可能的事情,这就是为什么我喜欢使用 EditorTemplate 方法来处理派生模型类型等。

提前致谢...

I have web application based on ASP.Net MVC3. I have a need for a "Create" view which will not know the model type until the user picks a sub-type to create from a drop-down. To attempt to solve this problem, I have created an editor template under Shared/EditorTemplates for each derived model type. This allows me to create a single "Create.cs" which is strongly-typed to a view model. The view model only has two members, an enum and a complex type. The idea is that the view will initially show only a drop-down (editor for the enum member) then when the user initially submits the specified "model type" (drop-down selected value), the POST action can check the "model type" specified and instantiate the correct derived model type for the view model's single complex member who's type is the base type for all possible "model types".

The abstract + derived type model objects...

public abstract class MyModelBase
{
    public MyModelType_e ModelType {get; set; }
    [Required]
    public string Name { get; set; }
}

public class DerivedType1 : MyModelBase
{ ... }

public class DerivedType2 : MyModelBase
{ ... }

public class DerivedType3 : MyModelBase
{ ... }

I have a complex view model as follows...

public enum MyModelType_e
{
    DerivedType1 = 0,
    DerivedType2 = 1,
    DerivedType3 = 2
}

public class MyModelCreate
{
    public MyModelType_e ModelTypeForSelectList { get; set; }
    public MyModelBase ModelBase { get; set; }
}

My GET controller action instantiates the above view model for the view i.e., only a drop-down list displayed with items based on the MyModelType_e enum + the value of the model's "ModelBase" property is initially null. So the GET action method looks like this...

[HttpGet]
public ActionResult Create()
{
    return View(new MyModelCreate());
}

Note the comment in caps bellow regarding the crux of my issue which is that TryUpdateModel fails (see below) even though it sets the properties of the ModelBase (derived-type) member to the corresponding form values as expected...

[HttpPost]
public ActionResult Create(MyModelCreate model)
{
    if (model.ModelBase == null || 
        (int)model.ModelTypeForSelectList != model.ModelBase.ModelType)
    {
        switch (model.ModelType)
        {
            case MyModelType_e.DerivedType1:
                model.ModelBase = new DerivedType1();
                break;
            case MyModelType_e.DerivedType2:
                model.ModelBase = new DerivedType2();
                break;
            case MyModelType_e.DerivedType3:
                model.ModelBase = new DerivedType3();
                break;
        }
        return View(model);
    }

    if (!TryUpdateModel(model.ModelBase))
        return View(model); // <<< THIS HAPPENS EVEN THOUGH ModelBase APPEARS TO BE UPDATED PROPERLY... 
    // For instance, I can see right here with intellisense that model.ModelBase.Name
    // is NOT null or empty but rather is truly updated with the correct form value(s)...

    // TODO: Insert the record, etc... (currently we never get here...)
}

So the above section is where the problem stems from but here is my view to help understand...

 @model MyNamespace.MyModelCreate

 <h2>Create</h2>

 ...

 @using (Html.BeginForm())
 {
     @Html.ValidationSummary(false)
    <fieldset>
    <legend>Input</legend>
    <div class="editor-label">
    @Html.Label("Select Model Type")
    </div>
    <div>
    @Html.EnumDropDownListFor(model => model.ModelType)
    @Html.ValidationMessageFor(model => model.ModelType)
    </div>

    @* 
    Conditionally show the correct editor template... 
    There is one existing under ../Shared/EditorTemplates for each
    derived type (DerivedType1, DerivedType2, DerivedType3, etc...)
    This much is working in the sense that the correct editor fields
    are displayed based on what the user selects in the above drop-down.
    *@
    @if (Model.InputModel != null)
    {  
        @Html.EditorFor(model => model.ModelBase);
    }

    <p>
        <input type="submit" value="Continue" />
    </p>              
    </fieldset>
}

So once with the initial POST (model type is selected), my POST action method falls through to the TryUpdateModel line by design but for some reason the validation fails. The part I really don't understand is that the validation summary reports "Name is required" even though I can clearly watch TryUpdateModel set the Name propery properly on the ModelBase member of the view model.

I'd greatly appreciate any help or guidance here... I am fairly new to ASP.NET MVC and I am open to doing things differently since I understand there are probably other ways I could design my requests/actions + views to accomplish this "multi-step" problem but I really am just going for the simplest possible thing which I why I like the EditorTemplate approach for handling the derived model types, etc.

Thanks in advance...

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

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

发布评论

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

评论(3

傻比既视感 2025-01-12 18:05:43

检查模型状态。 TryUpdateModel 运行后应该会设置错误。
我必须从某些属性中删除 Required 属性。

Check ModelState. There should be errors set after TryUpdateModel runs.
I had to remove a Required attribute from some properties.

不知在何时 2025-01-12 18:05:43

我的解决方案基本上涉及增加两个额外的控制器操作(GET 和 POST)。我在 ../Shared/EditorTemplates 下创建了强类型的单独视图(每个派生模型类型一个)。这样,初始的“Create”POST 操作仅接收选定的类型(模型只是指定所需类型的枚举值),然后重定向到另一个控制器操作,例如“CreateByType”,该操作获取所请求的具体类型的新实例并返回正确的编辑视图。附加的“CreateByType”控制器操作(GET 和 POST)仅需要处理抽象基础,因为它从服务层请求实例(传递枚举值)。然后我所要做的就是在 EditorTemplates 下为每种具体类型创建一个视图。

My solution basically involved growing two additional controller actions (GET and POST). I created separate views under ../Shared/EditorTemplates which are strongly typed (one for each derived model type). This way the initial "Create" POST action only receives the selected type (model is just an enum value specifying the desired type) and then redirects to another controller action like "CreateByType" which gets a new instance of the requested concrete type and returns the correct edit view. The additional "CreateByType" controller actions (GET and POST) only needs to deal with the abstract base because it requests the instance from the service layer (passing the enum value). Then all I had to do was create a view under EditorTemplates for each concrete type.

混吃等死 2025-01-12 18:05:43

我的模型中有 IHttpContextAccessor 。我在执行 await TryUpdateModelAsync() 之前将其填充到控制器中。我从 ModelState 读取错误,发现此访问器缺少响应的 ContentType 字段。只需添加 [ValidateNever] 属性即可解决我的问题。

I had IHttpContextAccessor in my model. I filled it in controller before executing await TryUpdateModelAsync(). I read errors from ModelState and found out this accessor was missing ContentType field for response. Simly adding [ValidateNever] attribute to it fixed my problems.

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