1

我现在已经多次遇到这个问题。我以不同的方式解决了这个问题,但总觉得有更好的方法来处理这种情况。寻找解决方案(最佳实践)已经取得了成果。希望得到一些圣人的建议。

场景是这样的:我正在构建一个 MVC4 Web 应用程序。我正在将一个复杂的模型传递给我的视图。例如:

public class Uat 
{
    public int UatId { get; set; }
    public int ProjectId { get; set; }
    public virtual Project Project { get; set; }
    public virtual ICollection<TestCase> TestCases { get; set; }
    public UatStatus Status { get; set; }
    public string StartDate { get; set; }
    public string StatusChangeDate { get; set; }
    public string ProductVersion { get; set; }
    public string Introduction { get; set; }
    public bool isActive { get; set; }
}

我想将此模型传递给视图并让用户仅更新模型的某些部分。在我正在处理的当前实例中,用户正在更新子对象(TestCase)的两个字段。TestCase 对象如下所示:

 public class TestCase
 {
    [Key]
    public int TestCaseId { get; set; }
    public int UatId { get; set; }
    public virtual Uat Uat { get; set; }
    public virtual ICollection<Step> Steps { get; set; }
    public TestCaseStatus Status { get; set; }
    public string Title { get; set; }
    public string Comment { get; set; }
}

用户只编辑TestCase.Comment 和TestCase.Status。大多数其他值已由管理员用户设置。所以我将一个 Uat 对象传递给我的视图,如下所示:

@Model ClientPortal.Models.Uat

@{
ViewBag.Title = "Uat";
}

<h2>Uat</h2>

@using (Html.BeginForm("UatEvalution", "Client")) 
{
@Html.ValidationSummary(true)
<div>
    @{var iteration = 0;}
    @foreach (var testCase in Model.TestCases)
    {
        <div class="well">
        <span class="testCaseTitle"><h4>@testCase.Title</h4></span> 

        <br />
        <br />

        <ol style="margin-left:40px">
            @foreach (var step in testCase.Steps)
            {
                <li>@step.Description</li>
            }
        </ol>

        <br />

        <input type="checkbox" name="TestCases[@iteration].Status" value="2" />Approve &nbsp; &nbsp; 
        <input type="checkbox" name="TestCases[@iteration].Status" value="1" />Deny 

        <br />

        <span><textarea name="TestCases[@iteration].Comment"></textarea></span>


        @{iteration = iteration + 1;
    }
        </div>
    }
</div>
<input type="submit" />
}

我的控制器动作看起来像这样:

    [HttpPost]
    public ActionResult UatEvalution(Uat uat)
    {
        //process data
        //Update(uat);
        //SaveChanges();
    }

问题是,我在控制器中的 uat 对象只有 TestCases 数据。其他所有内容为 null 或 0。Uat 中的 TestCases 仅包含 Comment 和 Status 的数据,其他所有内容均为 null 或 0。因此,如果我尝试在此模型上运行 Update,我会收到错误消息:具有相同键的对象ObjectStateManager 中已经存在。我已经研究过了,这对我来说很有意义。但是为了解决这个问题,我过去做了两件事之一:

  1. 我在视图中创建了一堆隐藏字段来填充模型中的其余属性,这样当模型传回控制器时,所有数据的填写方式与我从D b; 除了它具有用户所做的更改。

  2. 我从数据库中重新提取 TestCase 条目,对该条目进行适当的更改,然后 SaveChanges()。

这两种解决方案对我来说都很难看,而且我确信我做错了什么。虽然这在过去为我解决了这个问题,但它会导致我的视​​图被隐藏字段弄得杂乱无章,或者我最终向数据库发出了太多请求。我的应用程序很小,所以这些变通办法现在没有太大的效果,但我真的很想养成最佳实践的习惯。任何帮助将不胜感激,因为它总是来自 SO 社区。提前致谢!

4

1 回答 1

2

这就是为什么您需要一个代表实体的全部或部分的视图模型。使用视图模型的好处之一是防止发布过多或过少。你的情况就是一个很好的例子。您只需要更新实体的几个字段,这在大多数情况下实际上是有意义的,因此您不需要将其余字段返回到视图。甚至没有将它们作为隐藏字段,只是为了将值回发并保存在数据库中。如果您只需要两个字段,请为其创建一个模型,例如:

public class TestCaseModel
{
    public int TestCaseId { get; set; }
    public TestCaseStatus Status { get; set; }
    public string Comment { get; set; }
}

由于您将采用 viewmodel 路线,因此您可能还需要为您的Uat实体提供一个 viewmodel。或者最好的方法是有一个像这样的平面模型:

    public class TestCaseInputModel
    {
        public int UatId{ get; set; } // for reference
        public int TestCaseId { get; set; }
        public TestCaseStatus Status { get; set; }
        public string Comment { get; set; }
    }

然后只需添加必要的字段或您想要“显示”的字段,这意味着您不希望有输入。然后,您必须将模型的所有“输入”字段映射回您的实体。就像是:

var testCaseEntity = retrieve_from_db();
testCaseEntity.UatId = input.UatId;
testCaseEntity.Status = input.Status;
testCaseEntity.Comment  = input.Comment;

此外,您可以使用自动映射工具将模型中的值返回给实体。这是你能做到的最干净的方式,IMO。

于 2013-04-03T02:00:07.213 回答