【问题标题】:Create (not read) field values into a new view in C# MVC在 C# MVC 中创建(未读取)字段值到新视图中
【发布时间】:2014-01-11 18:20:27
【问题描述】:

我已经查看、尝试了几种不同的解决方案,但没有找到任何可行的方法(至少,没有与我想要遵循的示例足够接近的示例)。我确定我错过了一些对于更有经验的编码人员来说很简单的东西。帮助?

我有一个名为 Residents 的模型。它包括 ResidentID、PFName、PLName。我有一个居民控制器。我对居民有 CRUD 意见。一切正常。

我有一个名为 Logs 的模型。它包括 LogID、ResidentID、Comments。我有一个日志控制器。我有日志的 CRUD 视图。一切正常。

我可以显示居民的所有日志条目。工作正常。创建日志条目后,我可以使用方法显示 PFName

@Html.DisplayFor(model => model.Resident.PFName) 

接下来,我想为选定的居民创建一个新的日志条目。

这就是我遇到问题的地方。我希望“创建”视图(用于日志)显示所选居民的 ResidentFName 和 ResidentLName,而不是 ResidentID。

此时,从居民的详细信息视图中,我有一个 CreateLog 链接。

@Html.ActionLink("New Log Entry", "../Log/Create", new { @ResidentID = Model.ResidentID})

这(可能不是最好的方法)给了我一个带有所选 ID 值的 URL

http://localhost:999/Log/Create?ResidentID=1

ResidentID 的值正确;它会根据选择的居民而变化。

这个值输入正确

        @Html.TextBoxFor(model => model.ResidentID)

在新的 CreateLog 页面上使用 Log Controller Create 操作。

public ActionResult Create(int ResidentID)

我计划隐藏 ResidentID 文本框,以便用户看不到它。看来我必须以表格形式提供它才能创建新的日志条目。

CreateLog 表单目前的工作方式与我现在的一样。我可以创建一个日志条目并验证该条目是否已为居民正确记录。

但是,我希望表单显示居民的 PFName 和 PLName,以便用户可以看到选择居民的反馈。

我相信我想要的相关数据(PFName 和 PLName)必须以某种方式传递给 CreateLog 表单。我无法从表格中得到它。

由于 ResidentID 只有未保存的条目,因此我无法使用 CreateLog 表单中的值来显示相关数据。如前所述,对于列表,不存在这样的问题。它仅适用于 CreateLog。

我已尝试将数据添加到 URL。不工作。我尝试在控制器(和 URL)中设置字符串。不工作。我看过设置一个 cookie,但从来没有这样做过,所以不确定要设置什么,把它放在哪里,或者如何从中获取值。我已经研究过在控制器中设置一个变量......(可以显示下拉列表,但我不需要一个可供选择的列表——我想要相关表中的匹配值)。

Log.LogID(PK, Identity)
Log.ResidentID(FK)
Resident.PFName
Resident.PLName

我可以在我的 SQLDB 中直接使用这些表/字段创建一个视图并对其进行更新。

【问题讨论】:

    标签: c# asp.net-mvc asp.net-mvc-5


    【解决方案1】:

    假设一个看起来像这样的视图模型:

    public class CreateLogViewModel
    {
        public int ResidentID { get; set; }
        public string PFName { get; set; }
        public string PLName { get; set; }
    
        public string SomeLogCreationProperty { get; set; }
        // other properties
    }
    

    您的控制器可能如下所示:

    public ActionResult Create(int ResidentID)
    {
        var model = db.Residents.Where(r => r.ResidentID == ResidentID)
                    .Select(r => new CreateLogViewModel
                    {
                        ResidentID = r.ResidentID,
                        PFName = r.PFName,
                        PLName = r.PLName
                        // other properties
                    });
    
        return View(model);
    }
    

    然后是视图:

    @model CreateLogViewModel
    
    @using (Html.BeginForm())
    {
        @Html.HiddenFor(m => m.ResidentID)
        @Html.HiddenFor(m => m.PFName)
        @Html.HiddenFor(m => m.PLName)
    
        @Html.EditorFor(m => m.SomeLogCreationProperty)
        // other properties
        <input type="submit" />
    }
    

    然后这将 POST 回:

    [HttpPost]
    public ActionResult Create(CreateLogViewModel model)
    {
        if (ModelState.IsValid)
        {
            return RedirectToAction("Index");
        }
    
        // Redisplay the form with errors
        return View(model);
    }
    

    【讨论】:

    • 谢谢!我将不得不对其进行测试,但看起来您包含的“SomeLogCreationProperty”可能是缺失的部分。
    • 不幸的是,无法正常工作。我创建了一个包含所有必需字段的新类。我更改了控制器“创建”以包含 db 调用,现在出现错误。 '传入字典的模型项的类型为'System.Data.Entity.Infrastructure.DbQuery`1[DSet.Models.CreateLog]',但此字典需要'DSet.Models.CreateLog'类型的模型项。'我理解这个错误——它说我正在做我所做的事情。不过,不太确定如何修复它。
    • @Terri 您必须确保投影到 CreateLog 类型。即:Select(x =&gt; new CreateLog { ... })。但是,CreateLog 也不应该是实体类型。视图模型完全是一个独立的东西,专门设计用于表示数据的一部分。也就是说,视图模型代表了要显示的视图所需的所有数据。
    【解决方案2】:

    扩展 John H 和 StuartLC 的答案,您需要使用 ViewModels 和以下工作流程:

    Database->(load)->Model->Controller->(convert)->ViewModel->View
    

    View->ViewModel->Controller->(convert)->Model->(save)->Database
    

    假设您有以下模型:

    namespace Models
    {
        public class Residents
        {
            public int ResidentID { get; set; }
            public string PFName { get; set; }
            public string PLName { get; set; }
            //...
        }
    
        public class Logs
        {
            public int LogID { get; set; }
            public int ResidentID { get; set; }
            public string Comments { get; set; }
            //...
        }
    
    }
    

    您需要一个 ViewModel 来结合您在Log\CreateView 中显示输入所需的数据:

    namespace ViewModels
    {
        public class ResidentLog
        {
            public int ResidentID { get; set; }
            public string PFName { get; set; }
            public string PLName { get; set; }
            public string Comments { get; set; }
            //...
        }
    }
    

    然后在控制器内部:

    public class LogController : Controller
    {
        [HttpGet]
        public ActionResult Create(int ResidentID)
        {
            // Run in debug and make sure the residentID is the right one
            // and the resident exists in the database
            var resident = database.Residents.Find(residentID);
    
            var model = new ViewModels.ResidentLog
            {
                ResidentID = resident.ResidentID,
                PFName = resident.PFName,
                PLName = resident.PLName,
                Comments = string.Empty,
                // ...
            };
    
            // Run in debug and make sure model is not null and of type ResidentLog
            // and has the PFName and PLName
            return View(model);
        }
    
        [HttpPost]
        public ActionResult Create(ViewModels.ResidentLog model)
        {
            if (!ModelState.IsValid)
                return View(model);
    
            var log = new Models.Logs 
            { 
               // Assumes LogID gets assigned by database?
               ResidentID = model.ResidentID,
               Comments = model.Comments,
            };
    
            // Run in debug and make sure log has all required fields to save
            database.Logs.Add(log);
            database.SaveChanges();
    
            return RedirectToAction("Index"); // Or anywhere you want to redirect
        }
    }
    

    然后你的Log\CreateView:

    @model ViewModels.ResidentLog
    
    <!-- Display the values needed -->
    <div>@Model.ResidentID - @Model.PFName - @Model.PLName</div>
    
    @using (var form = Html.BeginForm(...))
    {
        <!-- This saves the values for the post, but in fact only ResidentID is actually used in the controller -->
        @Html.HiddenFor(m => m.ResidentID)
        @Html.HiddenFor(m => m.PFName)
        @Html.HiddenFor(m => m.PLName)
    
        @Html.EditorFor(m => m.Comments)
    
        <input type="submit" />
    }
    

    【讨论】:

    • 我需要返回 View(something) 的想法是有道理的,但是为控制器设置的变量不起作用。 r 存在“不存在”错误。 var model = new ViewModels.ResidentLog { ResidentID = r.ResidentID, PFName = r.PFName, PLName = r.PLName, Comments = string.Empty, // ... };
    • 抱歉,'r' 应该是 'resident',修正了上面的错字。
    • 工作!!!伊皮!所以刚刚发布的“不存在”问题是命名约定问题。解决了这个问题。所以在控制器'代码' var resident = db.Residents.Find(ResidentID); var nl = new NewLog { ResidentID = ResidentID, PFName = resident.PFName, PLName = resident.PLName, Comment = string.Empty, }; return View(nl);'代码'
    【解决方案3】:

    您需要向视图提供附加信息。 这可以通过至少两种方式完成

    1. 使用 ViewBag 动态作为 又快又脏 便宜又愉快的容器,从控制器传递视图所需的一切。
    2. (首选)使用自定义ViewModel 和定制类,该类包含视图所需的所有内容。这通常是首选,因为它是静态类型的。

    (我假设在调用 Log 控制器时 resident 已经保存在数据库中 - 您可能需要在其他地方获取它)

    因此,在您的日志控制器中,这是一个使用 ViewBag 的示例:

    [HttpGet]
    public ActionResult Create(int residentID)
    {
        ViewBag.Resident = Db.Residents.Find(residentId);
        return View();
    }
    

    然后您可以使用 ViewBag 在视图上显示常驻属性。

    编辑

    是的,我所说的坚持是指在 Db 中 - 为使用不明确的行话道歉。

    这是 ViewBag 方法的另一个示例(想法是为另一个对象创建一个新的Comment):

    以廉价 + 俗气的 ViewModel 方式执行此操作 - 在 HTTPGet 控制器创建方法中:

        public ActionResult Create(string objectType, int objectId)
        {
            // This is equivalent to youn fetching your resident and storing in ViewBag
            ViewModel.Object = FetchSomeObject(objectType, objectId);
            return View();
        }
    

    在视图中我使用这个(ViewBag 可以被控制器和视图访问):

    <title>@string.Format("Add new Comment for {0} {1}", ViewBag.Object.ObjectType, ViewBag.Object.Name);</title>
    

    正如您所说,您还需要在创建日志表单中为 ResidentId 添加隐藏

    根据@JohnH 的回答 (+1),更好的方法(比使用神奇的 ViewBag 动态)是专门为此屏幕创建自定义 ViewModelViewModel 可以两种方式重用(GET:Controller => View 和 POST:Browser => Controller,或者您甚至可以为 Get 和 Post 分支使用单独的 ViewModels

    【讨论】:

    • @StuartLC...你澄清了一个“缺失”的部分(现有的)。但不是解决方案。 CreateLog() 使用 db.Log(和日志控制器(应该如此)来创建新的日志条目,而不是 db.Residents。我正在通过 URL+Log 传递尚未创建的日志记录的 ResidentID控制器——所以在我想获取相关名称时,记录中不存在 ID;它只在视图中可见。我尝试的解决方案之一是创建一个组合模型/类以包含日志和常驻模型。我在其他视图中遇到了几个错误并恢复为 URL 方法(不太正确但没有错误)。
    • 我不太关注你。如果常驻实体在从LogController 生成LogCreate 视图时可用(例如持久化),那么您可以在ViewBag 中填充常驻实体,或者最好为视图填充自定义ViewModel。如果居民是同时在同一个屏幕上创建的,那么就需要一个浏览器端的解决方案,例如使用 javascript/jquery。
    • 似乎术语使问题变得模糊不清。在我创建日志条目时,居民的记录(包括 ID、PFName、PLName)存储在数据库中(我不清楚这是否等同于“持久”)。表单中没有存储居民信息(“?持久化?)。表单为空。我在控制器中使用 URL 和“public ActionResult Create(int ResidentID)”来“输入文本”(ResidentID)新表单。我尝试添加“ViewBag.Resident = db.Residents.Find(ResidentID);”按照您的建议进行操作。没有更改。名称均未显示。
    【解决方案4】:

    非常感谢大家,我已经成功了。最后一部分是告诉控制器返回模型(nl)。这是工作原理的完整规范:

    我创建了一个 ViewModel,其中包含

    public class NewLog
    {
        public int ResidentID { get; set; }
        public string PFName { get; set; }
        public string PLName { get; set; }
        public string Comment { get; set; }
        // other properties
    }
    

    LogController中,

    public ActionResult Create(int ResidentID)
    {
        var resident = db.Residents.Find(ResidentID);
    
        var nl = new NewLog
        {
            ResidentID = ResidentID,
            PFName = resident.PFName,
            PLName = resident.PLName,
            Comment = string.Empty,
        };
        return View(nl);
    }
    

    Create.cshtml页面中,

    @model My.Models.NewLog
    

    与新日志条目一起记录的所需居民 ID

            @Html.TextBoxFor(model => model.ResidentID, new {@Type = "Hidden"})
    

    以及相关的、用户友好的人名显示框

            @Html.DisplayFor(model => model.PFName)
            @Html.DisplayFor(model => model.PLName)
    

    而在用于访问创建页面的URL中,

        @Html.ActionLink("New Log Entry", "../Log/Create", new { @ResidentID = item.ResidentID, item.PFName, item.PLName})
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-12-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-08
      • 1970-01-01
      相关资源
      最近更新 更多