2016-11-12 82 views
-1

我有以下实体模型:如何执行一个复杂的对象模型,通过一个POST请求

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,MobileEmail的字段。 我的问题是,虽然AssetLabel作为FinderViewModel的一部分传递给视图,我可以显示来自AssetLabel的字段;图形对象AssetLabel不会在POST中传回。我知道我可以修改FinderViewModel,以便将Conversation作为一个属性,并将QRCode设置为单独的属性,该属性可以是表单中的隐藏字段,然后重新找到作为第二个处理的一部分的AssetLabel形式,但这看起来像很多工作,因为我已经验证过一次,以创建第二个表单(这就是为什么我要离开PHP MVC框架)。

第一个问题是如何?第二个问题是我以错误的方式接近这种设计模式。是否有更多的.NETty方法来通过多种形式保存数据?在我学习的这一刻,我并不想将信息存储在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代码段,用于AssetLabel

<input id="AssetLabel" name="AssetLabel" type="hidden"  
value="System.Data.Entity.DynamicProxies.AssetLabel_32653C4084FF0CBCFDBE520EA1FC5FE4F22B6D9CD6D5A87E7F1B7A198A59DBB3" 
/> 

回答

1

不能使用@Html.HiddenFor()产生了一个复杂的对象隐藏输出。在内部,方法使用.ToString()生成value(你情况下,输出是System.Data.Entity.DynamicProxies.AssetLabel_32653C4084FF0CBCFDBE520EA1FC5FE4F22B6D9CD6D5A87E7F1B7A198A59DBB3它不能绑定回一个复杂的对象)

您可以生成的AssetLabel的每个属性窗体控件 - 但是这将是不现实的您的情况,因为AssetLabel包含一个属性,其中包含Conversation的集合,而该集合又包含ConversationMessage的集合,因此您需要嵌套for循环来为每个属性ConversationConversationMessage生成一个输入。

但是,向客户端发送大量额外数据,然后再次将其全部发送回去会降低性能,将恶意用户的数据和数据结构的不必要细节暴露给恶意用户,恶意用户可能会更改数据)。

FinderViewModel应该只包含QRCode属性(或AssetLabel ID属性),并在视图

@Html.HiddenFor(m => m.QRCode) 

然后在POST方法,如果你需要的AssetLabel,再次从储备库中获得它就像你在GET方法中做的那样(尽管它不清楚为什么你需要在POST方法中使用AssetLabel)。

作为一个方面说明,视图模型应该只包含所需的视图属性,而不是包含在编辑数据时是数据模型(在你的情况从一个数据模型继承)属性。请参阅What is ViewModel in MVC?。根据您的看法,它应该包含(如果你想用它来编辑现有的对象和int? ID)4个属性FinderNameFinderMobileFinderEmailQRCode

+0

感谢斯蒂芬。 –

0

感谢斯蒂芬。 QRCode是AssetLabel上的PK和Conversation中的FK,因此需要通过工作流程进行跟踪。我试图保持视图模型通用的,因此是可以用于其他形式而不是把它紧紧地捆绑在一起,以这种特殊的形式和我试图通过周围的AssetLabel因为我已经做了它验证的显著量的状态,我不想重复。我制定了我需要做的事情 - 如果使用@ Html.Hidden(model => model.AssetLabel.QRCode),则表单字段名称将变为AssetLabel_QRCode,并自动映射到POST视图模型中的正确位置。为了促进代码重用并避免以后的返工,我在显示模板中创建了这个逻辑,其中的字段定义为hidden,然后使用重载方法@ Html.Partial(),该方法允许我定义表单名称的模型扩展

@Html.Partial 
(
    "./Templates/Assetlabel_hidden", 
    (GMSB.Models.AssetLabel)(Model.AssetLabel), 
    new ViewDataDictionary() 
    { 
     TemplateInfo = new TemplateInfo() 
     { 
      HtmlFieldPrefix = "AssetLabel" 
     } 
    } 
) 

但你是绝对正确的,这暴露了更多的领域和我的应用程序结构。我认为我会重新调整viewModel以暴露必要的字段并将AssetLabel验证移动到可从初始POST和后续帖子中调用的单独私有函数。这确实意味着控制器中需要额外的代码,因为平面vm字段需要手动映射到复杂的对象图。

相关问题