c# - 如何绑定(bind)外键?如何在 Controller 类中创建带有外键的模型对象?

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

我有以下表关系:

ProfileMeta 1 ----- 0...1 ProfileDetail

我在 Profile/Create 页面上单击提交后出现运行时错误

Cannot insert the value NULL into column 'ID', table 'ContosoUniversity1.dbo.ProfileMeta'; column does not allow nulls. INSERT fails.

我在 Models/ProfileDetail.cs 中将 ProfileMeta 正确引用为外键:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Web;

namespace ContosoUniversity.Models
{
    //ProfileMeta is Principal Class
    //ProfileDetail is Dependent Class
    public class ProfileDetail
    {

        //classNameID or ID is interpreted by EF as PK.
        public int ID { get; set; }

        //ForeignKey("<Navigation Property Name>")
        [Key, ForeignKey("ProfileMeta")] 
        [DatabaseGeneratedAttribute(DatabaseGeneratedOption.None)]
        public int ProfileMetaID {get; set;}
        public string UserName { get; set; }
        public string Age { get; set; }
        public string Location { get; set; }
        public string Gender { get; set; }

        //Optional Details
        public string HighSchool { get; set; }
        public string UndergraduateSchool { get; set; }
        public string GraduateSchool { get; set; }

        public virtual ProfileMeta ProfileMeta { get; set; }


    }

}

模型/ProfileMeta.cs:

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Web;

namespace ContosoUniversity.Models
{
    //ProfileMeta is Principal
    //ProfileDetail is Dependent
    public class ProfileMeta
    {

        public int ID { get; set; }
        public string Username { get; set; }
        public string password { get; set; }

        public virtual ProfileDetail ProfileDetail {get; set;}
        public virtual ICollection<MessageDetail> MessageDetails { get; set; }
        public virtual ICollection<ConversationMeta> ConversationMetas { get; set; }
    }
}

对于功能性注册/注册页面,我创建了一个模型:“Register”,它引用了 ProfileMeta 和 ProfileDetail。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace ContosoUniversity.Models
{
    public class Register
    {
        public ProfileMeta ProfileMeta_ { get; set; }
        public ProfileDetail ProfileDetail_ { get; set; }
    }
}

Views/Profile/Create.cshtml:

@model ContosoUniversity.Models.Register
@{
    ViewBag.Title = "Create";
}

<h2>Create</h2>


@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        <h4>Profile</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div class="form-group">
            @Html.LabelFor(model => model.ProfileMeta_.Username, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.ProfileMeta_.Username, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.ProfileMeta_.Username, "", new { @class = "text-danger" })
            </div>
        </div>

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

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

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

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

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

        <div class="form-group">
            @Html.LabelFor(model => model.ProfileDetail_.GraduateSchool, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.ProfileDetail_.GraduateSchool, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.ProfileDetail_.GraduateSchool, "", new { @class = "text-danger" })
            </div>
        </div>
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

配置文件 Controller .cs:

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create(Register register)
    {
        if (ModelState.IsValid)
        {
            //Add 1 ProfileMeta row and 1 linked ProfileDetail row
            ProfileMeta profileMeta = new ProfileMeta();

            profileMeta.Username = register.ProfileMeta_.Username;
            profileMeta.password = register.ProfileMeta_.password;

            ProfileDetail profileDetail = new ProfileDetail();
            //profileDetail.ID = register.ProfileDetail_.ID;

            //How to assign FK below?
            profileDetail.ProfileMetaID = register.ProfileDetail_.ID;
            profileDetail.UserName = register.ProfileDetail_.UserName;
            profileDetail.Age = register.ProfileDetail_.Age;
            profileDetail.Location = register.ProfileDetail_.Location;
            profileDetail.ProfileMeta = profileMeta;
            //profileDetail.UserName = register.ProfileDetail_.UserName;
            //profileDetail.Age = register.ProfileDetail_.Age;
            //profileDetail.Location = register.ProfileDetail_.Location;


            profileMeta.ProfileDetail = profileDetail;               

            db.ProfileMetas.Add(profileMeta);
            db.ProfileDetails.Add(profileDetail);

            db.SaveChanges();
            return RedirectToAction("Index");
        }

        return View(register);
    }

为什么我不能添加一行 ProfileMeta 和相应的一行 ProfileDetail?我以为数据库会自动生成主键(或 ID)。

是否有必要在 Controller 中为给定的模型对象显式设置导航属性? 另外,我是否需要在我创建的 ProfileDetail 对象中显式设置外键:“ProfileMetaID”?

最佳答案

首先,我想建议使用 EF 代码约定,而不是在 EF 中显式定义关系(您的声明是正确的,这只是我个人的偏好,我认为它更简洁)。

所以不是这个:

    //ForeignKey("<Navigation Property Name>")
    [Key, ForeignKey("ProfileMeta")] 
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.None)]
    public int ProfileMetaID {get; set;}
    public virtual ProfileMeta ProfileMeta { get; set; }

你可以这样做:

 public int ProfileMetaID { get; set; }
 public virtual ProfileMeta profileMeta { get; set; }

EF 将在数据库中自动为您创建 FK

至于您的问题,我强烈建议您使用 ViewModel 并包含您想要使用的所有属性。但是如果你想使用你现有的模型,你可以使用 ProfileMeta 作为你的基础模型并使用 model.profileDetail.Age 绑定(bind) ProfileDetail 的值>

举个例子:

您的模型(短版)

public class ProfileDetail
    {

        [Key, DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
        public int ID { get; set; }
        public string UserName { get; set; }
        public string Age { get; set; }
        public string Location { get; set; }
        public string Gender { get; set; }
        public int ProfileMetaID { get; set; }
        public virtual ProfileMeta profileMeta { get; set; }

    }
    public class ProfileMeta
    {

[Key, DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
            public int ID { get; set; }
            public string Username { get; set; }
            public string password { get; set; }
            public virtual ProfileDetail profileDetail {get; set;}
        }

您的 Controller : (无需显式设置属性值,因为关系和 EF 会处理它。)

[HttpGet]
        public ActionResult Create()
        {
            return View(new ProfileMeta());
        }
     [HttpPost]

     [ValidateAntiForgeryToken]
         public ActionResult Create(Register register)
            {
                if (ModelState.IsValid)
                 {            
                    db.ProfileMetas.Add(profileMeta);
                    db.ProfileDetails.Add(profileDetail);
                    db.SaveChanges();
                 }
           }

在您的 View 中(只是一个快照)

 <div class="form-group">
            @Html.LabelFor(model => model.Username, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Username, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Username, "", 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.profileDetail.Age, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.profileDetail.Age, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.profileDetail.Age, "", new { @class = "text-danger" })
            </div>
        </div>

最后这里有一个 fiddle 来说明整个概念:https://dotnetfiddle.net/N0prMA

在 fiddle 中,您会看到如果您提供年龄,它会提交回来并显示在页面中。

关于c# - 如何绑定(bind)外键?如何在 Controller 类中创建带有外键的模型对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29710434/

相关文章:

c# - 使用 protobuf-net 序列化数据集时出错

asp.net-mvc - 如何使这个 ASP.NET MVC Controller 更易于测试?

c# - Prism:如何使用 Unity Bootstrapper 在 Shell 区域中加载默认 View

c# - 在 XAML\Windows 8 商店应用程序中数据绑定(bind)可为 null 的类型

c# - 可以将所有需要的文件打包到 EXE 中吗?

c# - 如何将具有一个键和多个值的列表绑定(bind)到 GridView 或 Repeater

asp.net - 是否可以在 GridView (ASP.NET) 上执行 linq 查询?

javascript - DOM 更改后,单击时 jQuery 不起作用

javascript - 在 jQuery 中使用模型中的 bool

asp.net - 绕过表单例份验证自动重定向到登录,如何?