我实际上想出了一个如何在MVC中显示(GET)和编辑(POST)由三个模型组成的视图模型的工作示例。但是,我的MVC技能有限,我正在寻找关于"正确方法的建议"我应该这样做。 有问题的部分是我将表单字段单独发送回而不是视图模型,这是我无法弄清楚如何做的事情。
以下是我的模特
命名
public partial class Name
{
public Name()
{
this.Addresses = new HashSet<Address>();
this.Emails = new HashSet<Email>();
}
public int ID { get; set; }
public string FIRST_NAME { get; set; }
public string LAST_NAME { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
public virtual ICollection<Email> Emails { get; set; }
}
地址
public partial class Address
{
public int ADDRESS_ID { get; set; }
public int NameID { get; set; }
public string ADDRESS_1 { get; set; }
public string CITY { get; set; }
public string STATE { get; set; }
public string ZIP { get; set; }
public virtual Name Name { get; set; }
}
电子邮件
public partial class Email
{
public int EMAIL_ID { get; set; }
public int NameID { get; set; }
public string EMAIL { get; set; }
public virtual Name Name { get; set; }
}
我的视图模型由三个模型中的所有字段组成。
public class ContactFormViewModel
{
public int? ID { get; set; }
public string FIRST_NAME { get; set; }
public string LAST_NAME { get; set; }
public string ADDRESS_1 { get; set; }
public string CITY { get; set; }
public string STATE { get; set; }
public string ZIP { get; set; }
public string EMAIL { get; set; }
}
编辑页面的GET方法(在控制器中)
// GET: Names/Edit/5
//The GET method takes the id from the URL and passes it into the query to return data for the specific record
public ActionResult Edit(int id)
{
//This query is an outer join of the Name, Address and Email models/tables
var query = from n in db.Names
join a in db.Addresses
on n.ID equals a.NameID into na
from a in na.DefaultIfEmpty()
join e in db.Emails
on n.ID equals e.NameID into ne
from e in ne.DefaultIfEmpty()
where n.ID == id
//Creates a new instance of the view model, populated with the query data
select new ContactFormViewModel
{
ID = id,
FIRST_NAME = n.FIRST_NAME,
LAST_NAME = n.LAST_NAME,
ADDRESS_1 = a.ADDRESS_1,
CITY = a.CITY,
STATE = a.STATE,
ZIP = a.ZIP,
EMAIL = e.EMAIL
};
//Returns the query to the view
return View(query);
}
“编辑”页面的POST方法(在控制器中)
// POST: Names/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
//The POST method takes the individual form field data and passes it to queries that update all three models separately
public ActionResult Edit(int id, string FIRST_NAME, string LAST_NAME, string ADDRESS_1, string CITY, string STATE, string ZIP, string EMAIL)
{
if (ModelState.IsValid)
{
//Query the database for the row to be updated.
var queryN =
from n in db.Names
where n.ID == id
select n;
var queryA =
from a in db.Addresses
where a.NameID == id
select a;
var queryE =
from e in db.Emails
where e.NameID == id
select e;
//Assign the form field data to the fields in the model
foreach (Name n in queryN)
{
n.FIRST_NAME = FIRST_NAME;
n.LAST_NAME = LAST_NAME;
}
//If there are no address records, insert
if (!queryA.Any())
{
//New instance of Address
var address = new Address
{
NameID = id,
ADDRESS_1 = ADDRESS_1,
CITY = CITY,
STATE = STATE,
ZIP = ZIP
};
db.Addresses.Add(address);
}
//Else, if there are address records, then update
else
{
foreach (Address a in queryA)
{
a.ADDRESS_1 = ADDRESS_1;
a.CITY = CITY;
a.STATE = STATE;
a.ZIP = ZIP;
}
}
//If there are no email records, insert
if (!queryE.Any())
{
//New instance of Email
var email = new Email
{
NameID = id,
EMAIL = EMAIL
};
db.Emails.Add(email);
}
//Else, if there are email records, then update
else
{
foreach (Email e in queryE)
{
e.EMAIL = EMAIL;
}
}
//// Submit the changes to the database.
try
{
db.SaveChanges();
}
catch (Exception ex)
{
Console.WriteLine(ex);
// Provide for exceptions.
}
}
return RedirectToAction("Index");
}
视图
@model IQueryable<MDTestApplication.ViewModel.ContactFormViewModel>
@{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Name</h4>
<hr />
@*Uses foreach loop to get all field data from the view model*@
@foreach (var item in Model)
{
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.HiddenFor(modelItem => item.ID)
<div class="form-group">
@Html.LabelFor(modelItem => item.FIRST_NAME, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@*Using razor syntax to output the value*@
@*Using form field 'Name' attribute for posting back to the controller*@
<input type="text" name="FIRST_NAME" value="@item.FIRST_NAME" class="form-control" />
</div>
</div>
<div class="form-group">
@Html.LabelFor(modelItem => item.LAST_NAME, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
<input type="text" name="LAST_NAME" value="@item.LAST_NAME" class="form-control" />
</div>
</div>
<div class="form-group">
@Html.LabelFor(modelItem => item.ADDRESS_1, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
<input type="text" name="ADDRESS_1" value="@item.ADDRESS_1" class="form-control" />
</div>
</div>
<div class="form-group">
@Html.LabelFor(modelItem => item.CITY, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
<input type="text" name="CITY" value="@item.CITY" class="form-control" />
</div>
</div>
<div class="form-group">
@Html.LabelFor(modelItem => item.STATE, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
<input type="text" name="STATE" value="@item.STATE" class="form-control" />
</div>
</div>
<div class="form-group">
@Html.LabelFor(modelItem => item.ZIP, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
<input type="text" name="ZIP" value="@item.ZIP" class="form-control" />
</div>
</div>
<div class="form-group">
@Html.LabelFor(modelItem => item.EMAIL, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
<input type="text" name="EMAIL" value="@item.EMAIL" class="form-control" />
</div>
</div>
}
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
更新
我在Alex的回答中添加了额外的插入和更新代码。您可以将相同的设置用于地址和电子邮件。
foreach (var address in model.Addresses)
{
var addressToUpdate = name.Addresses.FirstOrDefault(a => AddressId== address.AddressId);
if (addressToUpdate != default(Address))
{
// preform an update
addressToUpdate.AddressId = address.AddressId;
addressToUpdate.City = address.City;
addressToUpdate.State = address.State;
addressToUpdate.Zip = address.Zip;
}
else
{
//perform an insert
var newAddress = new Address
{
NameID = model.ID,
Address1 = address.Address1,
City = address.City,
State = address.State,
Zip = address.Zip
};
db.Addresses.Add(newAddress);
}
}
答案 0 :(得分:3)
首先让我从命名约定开始。
此:
public int ADDRESS_ID { get; set; }
public int NameID { get; set; }
BAD 您根本没有命名约定,一些属性是PascalCase,其他属性是带下划线的大写字母。我强烈建议您安装一些工具,强制您应用样式和一致性规则集(例如StyleCop)。通常,将PascalCase用于属性是很常见的。
应用后,您的模型将如下所示:
public partial class Address
{
public int AddressId { get; set; }
public int NameId { get; set; }
public string Address1 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
public virtual Name Name { get; set; }
}
第二件事: 如果我理解正确,您正在尝试为一个用户编辑数据: 他(或她的)姓名,地址列表和电子邮件。如果我是对的,你的View和ViewModel都是错误的。您的ViewModel可能如下所示:
public class ContactFormViewModel
{
public int NameId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public IList<Address> Addresses { get; set; }
public IList<Emails> { get; set; }
}
控制器(的修订版强>):
// GET: Names/Edit/5
//The GET method takes the id from the URL and passes it into the query to
//return data for the specific record
public ActionResult Edit(int id)
{
//You don't need the joins since you have navigation properies!
var name = db.Names.FirstOrDefault(n => n.ID == id);
ContactFormViewModel model;
if(name == default(Name))
{
model = new ContactFormViewModel{
Addresses = new List<Address>(),
Emails = new List<Email>()
};
}
else {
model = new ContactFormViewModel
{
NameId = name.NameId ,
FirstName = name.FirstName,
LastName = name.LastName ,
Addresses = name.Addresses.ToList(),
Emails = name.Emails.ToList(),
};
}
if(!model.Addresses.Any())
{
model.Addresses.Add(new Address());
}
if(!model.Emails.Any())
{
model.Emails.Add(new Email());
}
//Returns the query to the view
return View(model);
}
查看:
@model MDTestApplication.ViewModel.ContactFormViewModel
@{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Name</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.HiddenFor(model => model.NameId)
<div class="form-group">
@Html.LabelFor(model => model.FirstName, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.FirstName, new { @class = "FirstName" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.LastName, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => item.LastName, new { @class = "FirstName" })
</div>
</div>
@for (int i = 0; i < Model.Addresses.Count; i++)
{
@Html.HiddenFor(model => model.Addresses[i].AddressId)
<div class="form-group">
@Html.LabelFor(model => model.Addresses[i].Address1, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Addresses[i].Address1, new { @class = "FirstName" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Addresses[i].City, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Addresses[i].City, new { @class = "FirstName" })
</div>
</div>
/// Continue here with all the address properties
}
@for (int i = 0; i < Model.Emails.Count; i++)
{
@Html.HiddenFor(model => model.Emails[i].EmailId)
<div class="form-group">
@Html.LabelFor(model => model.Emails[i].Email, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Emails[i].Email, new { @class = "FirstName" })
</div>
</div>
}
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
在控制器中编辑操作:
public ActionResult Edit(ContactFormViewModel model)
{
if (ModelState.IsValid)
{
//Query the database for the row to be updated.
var name = db.Names.FirstOrDefault( n => n.NameId == model.NameId);
if(name != default(Name))
{
name.FirstName = model.FirstName;
name.LastName = model.LastName;
bool hasAddresses = name.Addresses.Any();
foreach(var address in model.Addresses)
{
var addressToUpdate = name.Addresses.FirstOrDefault(a => a.AddressId == address.AddressId);
if(addressToUpdate != default(Address))
{
// preform an update
}
else
{
//perform an insert
}
}
foreach(var email in model.Emails)
{
var emailToUpdate = name.Emails.FirstOrDefault(a => a.EmailId == email.EmailId);
if(emailToUpdate != default(Email))
{
// preform an update
}
else
{
//perform an insert
}
}
db.SaveChanges();
}
}
}
答案 1 :(得分:0)
您尝试做的是提交一个可变长度列表,这不是asp mvc可以自己处理的。让我解释一下:
您有一个表单,表单内部是一个foreach循环,可为多个记录创建可编辑字段。 ASP MVC可以使用其表单助手处理更新单个记录,但是当您有多个需要更新的记录时,ASP MVC不会自动为每个记录创建索引编号,这使得它无法跟踪哪个属性属于哪个记录。提交表单时,MVC不会为您创建此索引,因此您需要找到替代解决方案。
我强烈建议您查看此博客并合并为实现表单提交而提供的帮助程序:
http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/