填充 ViewModel 的正确方法?

JD *_*vis 4 c# asp.net-mvc entity-framework viewmodel asp.net-mvc-5

我正在开发一个网络应用程序来工作,并且我正在使用标准 CRUD 风格的交互。但是,我不希望用户更新某些字段,因此我将它们从视图中删除了。但是,如果我没有显式设置这些字段,那么当模型在数据库中更新时它们会被清除。

我关心填充 ViewModel 字段的正确方法是什么。

我想出的粗略想法是这样的:

我的视图模型:

public class EditSoftwareTrackingViewModel 
{
    public EditSoftwareTrackingViewModel(SoftwareTracking model)
    {
        Id = model.Id;
        SoftwareId = model.SoftwareId;
        ComputerId = model.ComputerId;
        SoftwareActionId = model.SoftwareActionId;
        LastModified = model.LastModified;
        Computer = model.Computer;
        Software = model.Software;
        SoftwareAction = model.SoftwareAction;
    }
    public int Id { get; set; }
    [DisplayName("Software")]
    public int SoftwareId { get; set; }
    [DisplayName("Computer")]
    public int ComputerId { get; set; }
    [DisplayName("Software Action")]
    public int SoftwareActionId { get; set; }
    [DisplayName("Last Modified")]
    public DateTime? LastModified { get; set; }

    public virtual Computer Computer { get; set; }
    public virtual Software Software { get; set; }
    public virtual SoftwareAction SoftwareAction { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我的主要模型

[Table("asset.SoftwareTracking")]
public partial class SoftwareTracking
{
    public int Id { get; set; }
    [DisplayName("Software")]
    public int SoftwareId { get; set; }
    [DisplayName("Computer")]
    public int ComputerId { get; set; }
    [DisplayName("Date Entered")]
    public DateTime? EnteredDate { get; set; }
    [DisplayName("Software Action")]
    public int SoftwareActionId { get; set; }
    [DisplayName("Last Modified")]
    public DateTime? LastModified { get; set; }

    public virtual Computer Computer { get; set; }
    public virtual Software Software { get; set; }
    public virtual SoftwareAction SoftwareAction { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我的控制器使用视图模型

    public ActionResult Edit(int? id)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }
        EditSoftwareTrackingViewModel softwaretracking = new EditSoftwareTrackingViewModel(db.SoftwareTrackings.Find(id));
        if (softwaretracking == null)
        {
            return HttpNotFound();
        }
        GeneratePageData(softwaretracking.Software.Id);
        return View(softwaretracking);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Edit(EditSoftwareTrackingViewModel softwaretracking)
    {
        if (ModelState.IsValid)
        {
            softwaretracking.LastModified = DateTime.Now;
            var softwareTrack = db.SoftwareTrackings.Find(softwaretracking.Id);
            softwareTrack = new SoftwareTracking
            {
                Computer = softwaretracking.Computer,
                ComputerId = softwaretracking.ComputerId,
                LastModified = softwaretracking.LastModified,
                Software = softwaretracking.Software,
                SoftwareAction = softwaretracking.SoftwareAction,
                SoftwareActionId = softwaretracking.SoftwareActionId,
                SoftwareId = softwaretracking.SoftwareId,
                EnteredDate = softwareTrack.EnteredDate
            };

            db.Entry(softwareTrack).State = EntityState.Modified;
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        GeneratePageData(softwaretracking.Software.Id);
        return View(softwaretracking);
    }
Run Code Online (Sandbox Code Playgroud)

有更好的选择吗?或者我应该继续以这种方式创建我的视图模型?

编辑

我的业务逻辑和观点

    private void GeneratePageData(int? id = null)
    {

        ViewBag.Computers = new SelectList(db.Computers, "Id", "ComputerName");
        ViewBag.SoftwareActions = new SelectList(db.SoftwareActions, "Id", "ActionPerformed");

        var usedSoft = (from softTrack in db.SoftwareTrackings
                        where (softTrack.SoftwareActionId != 3)
                        select softTrack.Software);

        var softwareList = (from soft in db.Softwares
                            where (
                                ((from softTrack in db.SoftwareTrackings
                                  where (softTrack.SoftwareActionId != 3 && softTrack.SoftwareId == soft.Id)
                                  select softTrack.Software).Count() < soft.KeyQuantity)
                                && !(soft.AssetStatusId == 4 || soft.AssetStatusId == 5)
                                || soft.Id == id)
                            select soft).ToList();

        ViewBag.SoftwareList = softwareList.Select(t => new SelectListItem
        {
            Text = t.SoftwareIdNameFull,
            Value = t.Id.ToString()
        });

    }
Run Code Online (Sandbox Code Playgroud)

还有我的看法

@model Lighthouse_Asset_Manager.Models.EditSoftwareTrackingViewModel

@{
    ViewBag.Title = "Edit Software Install";
    Layout = "";
}

<div class="modal-header">
    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">
        &times;
    </button>
    <h4 class="modal-title" id="myModalLabel">Edit Software Install</h4>
</div>
<div class="modal-body">

    @using (Html.BeginForm(null, null, FormMethod.Post, new { id = "computerForm" }))
    {
        @Html.AntiForgeryToken()
        @Html.HiddenFor(model => model.Id)

        <div class="form-horizontal">
            @Html.ValidationSummary(true)


            <div class="form-group">
                @Html.LabelFor(model => model.SoftwareId, "Software", new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.DropDownList("SoftwareId", (IEnumerable<SelectListItem>)ViewBag.SoftwareList, "-- Select --", new
                    {
                        @style = "width:100%",
                        @class = "select2"
                    })
                    @Html.ValidationMessageFor(model => model.SoftwareId)
                </div>
            </div>

            <div class="form-group">
                @Html.LabelFor(model => model.ComputerId, "Computer", new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.DropDownList("ComputerId", (IEnumerable<SelectListItem>)ViewBag.Computers, "-- Select --", new
                    {
                        @style = "width:100%",
                        @class = "select2"
                    })
                    @Html.ValidationMessageFor(model => model.ComputerId)
                </div>
            </div>

            <div class="form-group">
                @Html.LabelFor(model => model.SoftwareActionId, "Action", new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.DropDownList("SoftwareActionId", (IEnumerable<SelectListItem>)ViewBag.SoftwareActions, "-- Select --", new
                    {
                        @style = "width:100%",
                        @class = "form-control"
                    })
                    @Html.ValidationMessageFor(model => model.SoftwareActionId)
                </div>
            </div>


            <div class="form-actions no-color">
                <button type="submit" class="btn btn-primary btn-sm"><i class="fa fa-floppy-o"></i> Edit Install Record</button>
                <button type="button" class="btn btn-default" data-dismiss="modal">
                    Cancel
                </button>
            </div>
        </div>
    }
</div>
Run Code Online (Sandbox Code Playgroud)

小智 5

使用视图模型的方法是一种很好的方法。这个问题的答案解释了一些好处,包括防止过度发布攻击、使用视图特定的显示和验证属性以及包括视图特定的属性,例如SelectLists. automapper等工具可以轻松地在数据和视图模型之间进行映射,并减少控制器中的代码。我建议您对视图模型进行一些更改。、和属性不是必需的(您不需要绑定到这些属性),我会将这些属性包含LastModified在模型中而不是 ViewBag中ComputerSoftwareSoftwareActionSelectList

查看型号

public class EditSoftwareTrackingViewModel 
{
  public int Id { get; set; }
  [Display(Name="Software")]
  public int SoftwareId { get; set; }
  [Display(Name="Computer")]
  public int ComputerId { get; set; }
  [Display(Name="Software Action")]
  public int SoftwareActionId { get; set; }
  public SelectList Computers { get; set; }
  public SelectList SoftwareActions{ get; set; }
  public SelectList SoftwareList{ get; set; }
}
Run Code Online (Sandbox Code Playgroud)

然后更改GeneratePageData()方法来接受视图模型

private void GeneratePageData(EditSoftwareTrackingViewModel model)
{
  model.Computers = new SelectList(db.Computers, "Id", "ComputerName");
  ....
Run Code Online (Sandbox Code Playgroud)

并在视图中(总是最好使用强类型助手)

@Html.DropDownListFor(m => m.SoftwareId, Model.SoftwareList, "-- Select --", new { @class = "select2" })
Run Code Online (Sandbox Code Playgroud)

还有其他一些需要注意的事情。

  • 您应该使用该[Display(Name="..")]属性(而不是 [DisplayName(..)]
  • 设置该LastModified属性时,应考虑使用 UCT时间。
  • Id视图中不需要属性的隐藏输入(假设您使用默认{controller}/{action}/{id}路由映射) - 它会添加到路由值中并且无论如何都会被绑定
  • 除非您特别想要id表单的属性,否则您可以使用@using(Html.BeginForm()) {
  • 您不需要第二个参数LabelFor()- 它可能只是 Html.LabelFor(m => m.SoftwareId, new { @class = "control-label col-md-2" })因为您已在[Display] 属性中指定了它

最后,如果您想进一步简化视图,您可以考虑使用自定义或 html 帮助程序,如此答案EditorTemplates中所示,这将允许您替换

<div class="form-group">
  @Html.LabelFor(model => model.SoftwareId, new { @class = "control-label col-md-2" })
  <div class="col-md-10">
    @Html.DropDownListFor(m => m.SoftwareId, Model.SoftwareList, "-- Select --", new { @class = "select2" })
    @Html.ValidationMessageFor(model => model.SoftwareId)
  </div>
</div>
Run Code Online (Sandbox Code Playgroud)

与(自定义EditorTemplate

@Html.EditorFor(m => m.SoftwareId, "BootstrapSelect", Model.SoftwareList)
Run Code Online (Sandbox Code Playgroud)

或(自定义HtmlHelper

@Html.BootstrapDropDownFor(m => m.SoftwareId, Model.SoftwareList)
Run Code Online (Sandbox Code Playgroud)