我有以下实体模型:
public class AssetLabel
{
public string QRCode { get; set; }
public string asset { get; set; }
public virtual IEnumerable<Conversation> Conversations { get; set; }
}
public class Conversation
{
public int ID { get; set; }
public virtual AssetLabel AssetLabel{ get; set; }
public string FinderName { get; set; }
public string FinderMobile { get; set; }
public string FinderEmail { get; set; }
public ConversationStatus Status{ get; set; }
public IEnumerable<ConversationMessage> Messages { get; set; }
}
public class ConversationMessage
{
public int ID { get; set; }
public DateTime MessageDateTime { get; set; }
public bool IsFinderMessage { get; set; }
public virtual Conversation Conversation { get; set; }
}
public enum ConversationStatus { open, closed };
public class FinderViewModel : Conversation
{/*used in Controllers->Found*/
}
我的 MVC 应用程序将提示对 POST 请求进行QRCode
。 然后,我验证此代码是否存在于数据库AssetLabel
,并且满足其他一些服务器端逻辑。然后,我需要请求用户联系人详细信息以创建新的Conversation
记录。目前,我有一个 GET 到控制器操作,该操作返回捕获代码的第一个表单。 如果这是有效的,那么我创建一个新FinderViewModel
,用QRCode
的对象填充AssetLabel
,并返回一个视图来使用虚拟机并显示Name
、Mobile
和Email
的字段。我的问题是,尽管AssetLabel
作为FinderViewModel
的一部分传递给视图,但我可以显示AssetLabel
中的字段;图形对象 AssetLabel
不会在 POST 中传回。我知道我可以修改FinderViewModel
,以便它将Conversation
作为一个属性,并将QRCode
设置为一个单独的属性,该属性可能是表单中的隐藏字段,然后重新找到AssetLabel
作为处理的一部分第二个表单,但这感觉像是很多工作,因为我已经验证过一次才能达到目的创建第二种形式(这就是我远离PHP MVC框架的原因(。
第一个问题是如何?,第二个问题是我是否以错误的方式接近这种设计模式。 还有更多.通过多种形式持久化数据的方法是什么? 在我学习的这一点上,我真的不想将信息存储在cookie中或使用ajax。
下面显示了第一个形式的 POST、第二个视图和第二个窗体 POST 的其余代码(简化以消除不相关的逻辑(。
public class FoundController : Controller
{
private ApplicationDbContext db = new ApplicationDbContext();
// GET: Found
public ActionResult Index()
{
AssetLabel lbl = new AssetLabel();
return View(lbl);
}
[HttpPost]
public ActionResult Index(string QRCode)
{
if (QRCode=="")
{
return Content("no value entered");
}
else
{
/*check to see if code is in database*/
AssetLabel lbl = db.AssetLables.FirstOrDefault(q =>q.QRCode==QRCode);
if (lbl != null)
{
var vm = new FinderViewModel();
vm.AssetLabel = lbl;
vm.Status = ConversationStatus.open;
return View("FinderDetails", vm);
}
else
{/*Label ID is not in the database*/
return Content("Label Not Found");
}
}
}
[HttpPost]
public ActionResult ProcessFinder(FinderViewModel vm)
{
/*
THIS IS WHERE I AM STUCK! - vm.AssetLabel == NULL even though it
was passed to the view with a fully populated object
*/
return Content(vm.AssetLabel.QRCode.ToString());
//return Content("Finder Details posted!");
}
FinderView.cshtml
@model GMSB.ViewModels.FinderViewModel
@{
ViewBag.Title = "TEST FINDER";
}
<h2>FinderDetails</h2>
@using (Html.BeginForm("ProcessFinder","Found",FormMethod.Post))
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Finder Details</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.HiddenFor(model => model.ID)
@Html.HiddenFor(model => model.AssetLabel)
<div class="form-group">
@Html.LabelFor(model => model.FinderName, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.FinderName, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.FinderName, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.FinderMobile, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.FinderMobile, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.FinderMobile, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.FinderEmail, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.FinderEmail, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.FinderEmail, "", new { @class = "text-danger" })
</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>
}
为资产标签呈现的 HTML 代码段
<input id="AssetLabel" name="AssetLabel" type="hidden"
value="System.Data.Entity.DynamicProxies.AssetLabel_32653C4084FF0CBCFDBE520EA1FC5FE4F22B6D9CD6D5A87E7F1B7A198A59DBB3"
/>
不能使用 @Html.HiddenFor()
为复杂对象生成隐藏输出。在内部,该方法使用 .ToString()
生成value
(在您的情况下,输出System.Data.Entity.DynamicProxies.AssetLabel_32653C4084FF0CBCFDBE520EA1FC5FE4F22B6D9CD6D5A87E7F1B7A198A59DBB3
无法绑定回复杂对象(
您可以为AssetLabel
的每个属性生成一个表单控件 - 但这在您的情况下是不现实的,因为AssetLabel
包含一个属性,而 是Conversation
的集合,而又包含ConversationMessage
的集合,因此您需要嵌套的for
循环来为Conversation
和ConversationMessage
的每个属性生成输入。
但是,向客户端发送大量额外数据,然后再次将其全部发送回原封不动会降低性能,向恶意用户公开有关数据和数据结构的不必要详细信息,恶意用户可能会更改数据(。
FinderViewModel
应该只包含 QRCode
的属性(或 AssetLabel
的 ID 属性(和视图中
@Html.HiddenFor(m => m.QRCode)
然后在 POST 方法中,如果您需要AssetLabel
,就像您在 GET 方法中一样从存储库中再次获取它(尽管不清楚为什么您需要在 POST 方法中AssetLabel
(。
作为旁注,在编辑数据时,视图模型应仅包含视图中所需的属性,而不应包含作为数据模型的属性(在您的情况下从数据模型继承(。请参阅什么是 MVC 中的视图模型?。根据您的视图,它应该包含 4 个属性FinderName
、FinderMobile
、FinderEmail
和 QRCode
(如果您想使用它来编辑现有对象,则包含 int? ID
(。
谢谢斯蒂芬。QRCode 是 AssetLabel 上的 PK 和对话中的 FK,因此需要通过工作流进行跟踪。 我试图保持 viewModel 通用,以便它可以用于其他表单,而不是将其紧密耦合到这个特定表单,并且我试图传递 AssetLabel,因为我已经对它的状态进行了大量的验证我不想重复。 我计算出了我需要做什么 - 如果你使用 @Html.Hidden(model => 模型。AssetLabel.QRCode(,则表单字段名称将变为AssetLabel_QRCode,并自动映射到 POST 视图模型中的正确位置。为了促进代码重用并避免以后进行任何返工,我在显示模板中创建了此逻辑,其中字段定义为隐藏,然后使用 overload 方法@Html.Partial((,该方法允许我将模型扩展定义为表单名称
@Html.Partial
(
"./Templates/Assetlabel_hidden",
(GMSB.Models.AssetLabel)(Model.AssetLabel),
new ViewDataDictionary()
{
TemplateInfo = new TemplateInfo()
{
HtmlFieldPrefix = "AssetLabel"
}
}
)
但是您是绝对正确的,这公开了其他字段和我的应用程序结构。 我想我将重新起草 viewModel 以仅公开必要的字段并将 AssetLabel 验证移动到一个单独的私有函数,该函数可以从初始 POST 和后续帖子调用。 这确实意味着控制器中有额外的代码,因为平面 vm 字段需要手动映射到复杂对象图。