【问题标题】:Entity A depends on Entity B, how to send all entity's B info to POST Create method of Entity A using MVC Code First实体 A 依赖于实体 B,如何使用 MVC Code First 将所有实体的 B 信息发送到实体 A 的 POST 创建方法
【发布时间】:2017-05-25 17:51:34
【问题描述】:

我有一个用户类,它有一个名称(唯一且必需)、密码(必需)和配置文件(必需)。 Profile 类有一个唯一且必需的名称。这两个类都有一个 Id 作为主键,同时首先使用代码生成数据库。

我想允许在我的页面中创建新用户,这里有一些代码可以做到这一点:

控制器

        // GET: Users/Create
    public ActionResult Create()
    {
        populateViewBagWithProfilesAsSelectListItem();
        return View();
    }

    private void populateViewBagWithProfilesAsSelectListItem()
    {
        IEnumerable<SelectListItem> profiles = db.Profiles.ToList().
            Select(x => new SelectListItem
            {
                Value = x.Id.ToString(),
                Text = x.Name
            });
        ViewBag.Profiles = profiles;
    }

    // POST: Users/Create
    // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
    // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create([Bind(Include = "Name,Password,Profile")] User user)
    {
        user.Profile = db.Profiles.Find(user.Profile.Id);
        ModelState.Clear();
        TryValidateModel(user);
        lock (UsersLocker)
        {
            if (ModelState.IsValid)
            {
                db.Users.Add(user);
                db.SaveChanges();
                return RedirectToAction("Index");
            }
        }

        populateViewBagWithProfilesAsSelectListItem();
        return View(user);
    }

创建视图

@model ProceduresRecord.Web.MVC.Models.User

@{
    ViewBag.Title = "Crear";
}

<h2>@ViewBag.Title</h2>


@using (Html.BeginForm())

{
@Html.AntiForgeryToken()

<div class="form-horizontal">
    <h4>Usuario</h4>
    <hr />
    @Html.ValidationSummary(true, "", new { @class = "text-danger" })

    <div class="form-group">
        @Html.LabelFor(model => model.Name, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.Password, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.EditorFor(model => model.Password, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(model => model.Password, "", new { @class = "text-danger" })
        </div>
    </div>

    <div class="form-group">
        @Html.LabelFor(model => model.Profile, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.DropDownListFor(model => model.Profile.Id, (IEnumerable<SelectListItem>)ViewBag.Profiles, htmlAttributes: new { @class = "form-control" })
            @Html.ValidationMessageFor(model => model.Profile.Name, "", new { @class = "text-danger" })
        </div>
    </div>

    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" value="Crear" class="btn btn-default" />
        </div>
    </div>
</div>
}

<div>
    @Html.ActionLink("Volver a la Lista", "Index")
</div>

这会导致这个页面:

如您所见,我从数据库填充配置文件,我向用户显示配置文件名称,一旦他选择一个并单击创建,该配置文件的 Id 将发送到控制器中的 POST 方法创建。然后我努力使用提供的 ID 从数据库中获取完整的配置文件,我将完整的配置文件分配给用户并重新验证 ModelState(以便它更改为 true ......这是错误的,因为配置文件没有名称到目前为止)。 这一切似乎都有效,但我想知道......

难道没有更好的方法吗?我的意思是,在填充 html 选择时,我已经从数据库中获取了配置文件,能够一次性将完整的配置文件发送到 POST 并避免此代码,这不是很棒吗:

        user.Profile = db.Profiles.Find(user.Profile.Id);
        ModelState.Clear();
        TryValidateModel(user);

如果有什么办法,请告诉我!

P.D:我正在尝试自学 MVC 和 Code First,任何建议都将不胜感激。

【问题讨论】:

  • 不要在视图中使用数据模型,尤其是在编辑时。为Name、Password` 和SelectedProfile 创建一个视图模型 - 请参阅 CodingYoshi 的回答(并且您问题中的最后 3 行代码都不需要)

标签: c# asp.net-mvc entity-framework


【解决方案1】:

创建用户时,您不需要完整的Profile,而只需要Profile 的ID。因此,您只需要发布用户信息和Profile Id。当您创建一个新用户并设置 Profile Id 属性时,EF 会为您解决:如果配置文件存在该密钥,它将使用它,否则它将抱怨找不到具有提供的外键的项目.

我还会为视图创建一个模型,如下所示:

public class UserModel
{
    public string UserName { get; set; }
    public string Password { get; set; }

    // This will be the selected profile id
    public string SelectedProfileId { get; set; }

    // fill this with all the profiles
    public IEnumerable<SelectListItem> AvailableProfiles { get; set; }
}

每个SelectListItem 都可以这样创建:

new SelectListItem
    {
        Value = ProfileId, // Whatever the property is
        Text = ProfileName,  // this will be displayed in dropdown
    });

在您的控制器中创建一个实例并将其发送到您的视图。

在您的视图中创建这样的下拉列表:

@Html.DropDownListFor(m => m.SelectedProfileId, 
                new SelectList(m.AvailableProfiles)), 
                "Select Profile")

然后在帖子中从SelectedProfileId 属性中获取选定的个人资料ID。

【讨论】:

  • 设置Selected = true 在绑定到模型属性时根本不执行任何操作(它被DropDownListFor() 方法忽略,因为它的SelectedProfileId 的值决定了选择的内容) - 否则这是正确的方法
  • 谢谢!即使 JamieD77 的回答更适合我缺乏知识,你的回答也非常有帮助!我不知道如何使用视图模型,但事实证明它非常简单且功能强大。谢谢!
【解决方案2】:

不确定您的 ModelState 错误是什么。但是您可能应该在您的User 实体上拥有一个名为ProfileId 的属性。如果没有,您可能应该添加一个。然后您可以将您的视图配置文件下拉菜单更改为

@Html.DropDownListFor(model => model.ProfileId, (IEnumerable<SelectListItem>)ViewBag.Profiles, htmlAttributes: new { @class = "form-control" })

然后将 Bind(Include) 的 Create 操作中的 Profile 更改为 ProfileId,您的 Create 操作就可以了

// POST: Users/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for 
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Name,Password,ProfileId")] User user)
{
    lock (UsersLocker)
    {
        if (ModelState.IsValid)
        {
            db.Users.Add(user);
            db.SaveChanges();
            return RedirectToAction("Index");
        }
    }

    populateViewBagWithProfilesAsSelectListItem();
    return View(user);
}

如果是我,我会创建一个带有 Id、Name、Password 和 ProfileId 属性的新视图模型类 UserViewModel,并将其来回传递给视图。

【讨论】:

  • 谢谢,这完全是我的错误,我的用户中只有一个 Profile 类型的属性,但是缺少 int 类型的 ProfileId,我不知道按照惯例我应该同时拥有这两者。 . 我什至没有想到 EF 仅通过拥有这两个属性来单独完成所有这些工作。非常感谢!
猜你喜欢
  • 2021-08-24
  • 1970-01-01
  • 1970-01-01
  • 2021-04-01
  • 1970-01-01
  • 2012-06-25
  • 2012-12-31
  • 2011-08-22
  • 2011-07-22
相关资源
最近更新 更多