空EF中的相关数据对象

时间:2019-04-16 16:29:06

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

您好,最近我将项目从核心2.1更新为2.2,之后,当从表单(编辑)发送带有孩子的对象时,该对象的值为null(请参见下图),并且ModelState.IsValid始终为false,因为正在验证子类中的null,请帮助!

https://i.ibb.co/hsmpc43/Screen-Shot-2019-04-16-at-11-03-17-AM.png

这是我的课:

public class New : BaseEntity
{
    public string Title { get; set; }
    public string Message { get; set; }
    [ForeignKey("TowerID"), Display(Name = "Edificio")]
    public virtual Tower Tower { get; set; }
    public int TowerID { get; set; }

}

我的控制器:

[BreadCrumb(Title = "Editar", Order = 1)]
public IActionResult Edit(int id)
{
    var news = _newBL.GetById(id);
    PopulateDropDowns();
    return View(news);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(New value)
{
    if (ModelState.IsValid)
    {
        value.Resources = _files;
        await _newBL.Update(value);
        return RedirectToAction(nameof(Index));
    }
    PopulateDropDowns();
    return View(value);
}

和我的峰会表格:

<form asp-action="Edit" onsubmit="return disableButton(this);">

            <div class="form-row">
                <div class="form-group col-md-3">
                    <label asp-for="TowerID"></label>
                    <div class="custom-select">
                        <select asp-for="TowerID" class="form-control" asp-items="ViewBag.TowerID">
                            <option>Seleccione</option>
                        </select>

                    </div>
                    <span asp-validation-for="TowerID" class="text-red"></span>
                </div>
                <div class="form-group col-md-2">
                    <label asp-for="CreatedDate">Creado en</label>
                    <label asp-for="CreatedDate" class="input-readonly" readonly>@Model.CreatedDate</label>
                    <input type="hidden" asp-for="CreatedDate" />
                </div>
            </div>
            <div class="form-row">
                <div class="form-group col-md-12">
                    <label asp-for="Title"></label>
                    <input asp-for="Title" class="form-control" placeholder="@ViewData.ModelMetadata.Placeholder" />
                    <span asp-validation-for="Title" class="text-red"></span>
                </div>
            </div>
            <div class="form-row">
                <div class="form-group col-md-12">
                    <label asp-for="Message"></label>
                    <textarea id="summernote" asp-for="Message" class="form-control" rows="3"></textarea>
                    <span asp-validation-for="Message" class="text-red"></span>
                </div>
            </div>
            <div class="form-row">
                <button type="submit" class="btn green m-1" id="save"><i class="fas fa-save"></i> Guardar</button>
                <button asp-action="Index" type="submit" class="btn blue-darken-1 m-1"><i class="fa fa-arrow-left"></i> Atras</button>
                <a  class="btn red m-1" data-toggle="modal" data-target="#dangerModal">
                    <i class="fa fa-trash"></i> Eliminar
                </a>
            </div>
        </form>

这只有在我更新到Core 2.2时才开始发生,也就是在我发送带有新值的编辑表单时出现。

预先感谢

更新:

我尝试将其更改回核心2.1并开始工作,但是当我切换回2.2时开始,塔对象来自带有所有null值的剃刀POST,但新闻类型却没有(参见图片)

2 个答案:

答案 0 :(得分:2)

好吧,您只发布了Tower的ID,并且确实在填充它。 Tower的其他所有内容均未发布,因此其他所有属性当然都是null或默认值。我不确定为什么您认为这只是在2.2中才开始发生,因为情况总是如此。实际上,无论您使用什么框架,情况都是如此。这可能是Django或Ruby on Rails,而Tower道具仍然都是null,因为您没有发布任何道具。

HTTP是无状态协议。什么都没有,但是请求本身带来了什么。这些内存使用的用途是没有记忆的,因为甚至不知道以前有过请求。您可能会在此处插入类似会话的内容,但这本质上是假状态。服务器存储与会话ID关联的数据,然后通过Cookie将会话ID传输到客户端。然后,客户端必须在随后的每个恢复会话的请求上,将包含会话ID的cookie传回。即使这样,数据本质上仍然是请求的一部分,它只是作为特定一组已保存的服务器端数据的标识符而不是整个数据集的标识符。

无论如何,只需对您应该进行的代码进行一些调整即可轻松解决此问题。首先,永远不要直接绑定到实体类。造成这种情况的原因有很多,但主要的担忧是发布过多。使用您现在拥有的代码,客户端可以更改发送的数据以更改诸如您实体的ID之类的东西,您最终将使用那个 id而不是原来的ID修改实体。实际上正在被编辑。与此相关的另一条规则是,出于许多相同的原因,您绝对不应直接保存用户发布的任何内容。

相反,您应该绑定到视图模型,并且该视图模型应仅包含您实际希望允许用户编辑的属性。然后,您从数据库中重新获取实体,并在最终保存该实体之前,将视图模型中发布的值映射到该实体上。

public async Task<IActionResult> Edit(NewViewModel value)
{
    if (!ModelState.IsValid)
    {
        PopulateDropDowns();
        return View(value);            
    }

    var entity = await _newBL.GetById(id);
    if (entity == null)
        return NotFound();

    entity.Title = value.Title;
    entity.Message = value.Message;
    entity.TowerID = value.TowerID;
    entity.Resources = _files;

    await _newBL.Update(entity);
    return RedirectToAction(nameof(Index));
}

如果愿意,可以使用AutoMapper之类的库来处理映射部分。

答案 1 :(得分:0)

要解决此问题,您需要通过在函数中添加诸如.Include(blog => blog.Posts)之类的东西,在GetById()中包括相关数据(子级)
文档: https://docs.microsoft.com/en-us/ef/core/querying/related-data
 或您需要激活延迟加载(默认情况下,延迟加载被禁用)
查看更多: https://www.learnentityframeworkcore.com/lazy-loading

更新:
如果模型为空,则需要为所有模型项添加一个隐藏输入,以在提交表单时保存其值。
示例:
<input type="hidden" asp-for="Tower.Id" />