我正在开发一个webapp工作,我正在使用标准的CRUD风格交互。但是,某些字段我不希望用户更新,因此我将其从视图中删除。但是,如果我没有明确设置这些字段,则在数据库中更新模型时会清除它们。
我关心为ViewModels填充字段的正确方法是什么。
我提出的粗略想法是这样的:
我的观点模型:
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; }
}
我的主要模特
[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; }
}
我的控制器使用视图模型
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);
}
有更好的选择吗?或者我应该继续以这种方式创建我的视图模型吗?
修改
我的业务逻辑和观点
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()
});
}
我的观点
@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">
×
</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>
答案 0 :(得分:4)
使用视图模型的方法很好。 this question的答案解释了一些好处,包括防止过度发布攻击,使用特定于视图的显示和验证属性以及包括视图特定属性(如SelectLists
)。 automapper等工具可以轻松地在数据和视图模型之间进行映射,并减少控制器中的代码。我建议您对视图模型进行一些更改。 LastModified
,Computer
,Software
和SoftwareAction
属性不是必需的(您没有绑定到这些属性),我会在模型中包含SelectList
属性而不是ViewBag
查看模型
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; }
}
然后更改GeneratePageData()
方法以接受视图模型
private void GeneratePageData(EditSoftwareTrackingViewModel model)
{
model.Computers = new SelectList(db.Computers, "Id", "ComputerName");
....
并且在视图中(总是更喜欢使用强类型助手)
@Html.DropDownListFor(m => m.SoftwareId, Model.SoftwareList, "-- Select --", new { @class = "select2" })
其他一些注意事项。
[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]
中指定了它
属性最后,如果您想进一步简化视图,可以考虑this answer中指示的自定义EditorTemplates
或html帮助程序,以便您替换
<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>
with(custom EditorTemplate
)
@Html.EditorFor(m => m.SoftwareId, "BootstrapSelect", Model.SoftwareList)
或(自定义HtmlHelper
)
@Html.BootstrapDropDownFor(m => m.SoftwareId, Model.SoftwareList)
答案 1 :(得分:2)
您应该使用AutoMapper使Model和ViewModel之间的映射更清晰。使用此代码首先创建映射器。
Mapper.CreateMap<SoftwareTracking, EditSoftwareTrackingViewModel>();
Mapper.CreateMap<EditSoftwareTrackingViewModel, SoftwareTracking>();
如果要从模型创建视图模型,请执行以下操作:
public ActionResult Edit(int? id)
{
SoftwareTracking tracking = db.SoftwareTrackings.Find(id);
EditSoftwareTrackingViewModel viewmodel =
Mapper.Map<SoftwareTracking, EditSoftwareTrackingViewModel>(tracking);
return View(viewmodel);
}
如果要将视图模型中的信息填充回模型,请执行此操作
public ActionResult Edit(EditSoftwareTrackingViewModel vm)
{
if (ModelState.IsValid)
{
vm.LastModified = DateTime.Now;
var softwareTrack = db.SoftwareTrackings.Find(softwaretracking.Id);
softwareTrack =
Mapper.Map<EditSoftwareTrackingViewModel, SoftwareTracking>(vm, softwareTrack);
db.Entry(softwareTrack).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
答案 2 :(得分:1)
要修补更新模型而不从Db加载对象。试试Attach:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(EditSoftwareTrackingViewModel softwaretracking)
{
if (ModelState.IsValid)
{
var 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.SoftwareTrackings.Attach(softwareTrack);
db.Entry(softwareTrack).Property(a => a.Computer).IsModified = true;
db.Entry(softwareTrack).Property(a => a.ComputerId).IsModified = true;
db.Entry(softwareTrack).Property(a => a.LastModified).IsModified = true;
db.Entry(softwareTrack).Property(a => a.Computer).IsModified = true;
db.Entry(softwareTrack).Property(a => a.Software).IsModified = true;
db.Entry(softwareTrack).Property(a => a.SoftwareAction).IsModified = true;
db.Entry(softwareTrack).Property(a => a.SoftwareActionId).IsModified = true;
db.Entry(softwareTrack).Property(a => a.SoftwareId).IsModified = true;
db.SaveChanges();
return RedirectToAction("Index");
}
GeneratePageData(softwaretracking.Software.Id);
return View(softwaretracking);
}
关于是否使用ViewModel或直接使用Model的第二个问题。这实际上是一个意见问题,每种方法都有其优点和缺点。我对此没有强烈的意见,我只想指出这些优点和缺点供你考虑:
答案 3 :(得分:1)
这是模型类
[Table("CURRENCY")]
public class CurrencyClass : ICurrency
{
private Int32 mCURRENCY_ID = default(Int32);
[Key]
public virtual Int32 CURRENCY_ID
{
get { return mCURRENCY_ID; }
set { mCURRENCY_ID = value; }
}
private string mCURRENCY_NAME = default(string);
public virtual string CURRENCY_NAME
{
get { return mCURRENCY_NAME;}
set { mCURRENCY_NAME = value;}
}
private string mCURRENCY_DESC = default(string);
public virtual string CURRENCY_DESC
{
get { return mCURRENCY_DESC; }
set { mCURRENCY_DESC = value; }
}
private string mCURRENCY_SYMBOLE = default(string);
public virtual string CURRENCY_SYMBOLE
{
get { return mCURRENCY_SYMBOLE; }
set { mCURRENCY_SYMBOLE = value; }
}
private Int32 mcreated_by = default(Int32);
public virtual Int32 created_by
{
get { return mcreated_by; }
set { mcreated_by = value; }
}
private DateTime mcreated_date = default(DateTime);
public virtual DateTime created_date
{
get { return mcreated_date; }
set { mcreated_date = value; }
}
private Int32 mmodified_by = default(Int32);
public virtual Int32 modified_by
{
get { return mmodified_by; }
set { mmodified_by = value; }
}
private DateTime mmodified_date = default(DateTime);
public virtual DateTime modified_date
{
get { return mmodified_date; }
set { mmodified_date = value; }
}
}
这是ViewModel
public class CurrencyViewModel
{
[Key]
public Int32 CURRENCY_Id { get; set; }
[Required(ErrorMessage="Currency Name is required")]
public string CURRENCY_NAME { get; set; }
[Required(ErrorMessage="Currency Description is required")]
public string CURRENCY_DESC { get; set; }
[Required(ErrorMessage = "Currency Symbole is Required")]
public string CURRENCY_SYMBOLE { get; set; }
}
这是行动
[HttpPost]
[ActionName("Create")]
public ActionResult Create(CurrencyViewModel vm)
{
if (!ModelState.IsValid)
{
return View("Create");
}
obj.CURRENCY_NAME = vm.CURRENCY_NAME;
obj.CURRENCY_DESC = vm.CURRENCY_DESC;
obj.CURRENCY_SYMBOLE = vm.CURRENCY_SYMBOLE;
obj.created_by = 1;
obj.created_date = DateTime.Now;
obj.modified_by = 1;
obj.modified_date = DateTime.Now;
db.Currencies.Add(obj);
db.SaveChanges();
return RedirectToAction("Index");
}