【问题标题】:Assigning UserId to Foreign Key = null将 UserId 分配给外键 = null
【发布时间】:2013-08-02 08:50:36
【问题描述】:

我正在使用 Entity Framework 5 和 MVC 4,以及使用 Code First 迁移的 .NET 4.5。我在 1 到零或 1 关系中的两个实体之间有外键。当我尝试为 Jogger 创建记录时,一切都崩溃了。我收到以下错误消息:

  • 外键是空引用
  • dbo.Jogger 不存在
  • 未处理的异常...没有元数据。

当我运行 EF 查看器时,导航关系是完美的。目前没有使用 Fluent API 代码。

用户类

     [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int UserId { get; set; }
    public string UserName { get; set; }

    // Other properties

    public int? JoggerId { get; set; }
    public virtual Jogger Jogger { get; set; }

慢跑课程

    [ForeignKey("UserId")]
    public int JoggerId { get; set; }

    public virtual UserProfile UserId { get; set; }

当 JoggerController 生成时,它会为 Get 和 Post 方法生成以下代码:

// GET: /Jogger/Create

    public ActionResult Create()
    {
        ViewBag.JoggerId = new SelectList(db.UserProfiles, "UserId", "UserName");
        return View();
    }

    //
    // POST: /Jogger/Create

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create(Jogger jogger)
    {



        if (ModelState.IsValid)
        {
            db.Jogger.Add(jogger);
            db.SaveChanges();
            return RedirectToAction("Index");
        }

        ViewBag.JoggerId = new SelectList(db.UserProfiles, "UserId", "UserName", jogger.JoggerId);
        return View(jogger);
    }

根据生成的代码,在 Jogger/Create 视图中没有 JoggerId 或 UserId 字段。

通常它在代码的 (ModelState.IsValid) 部分失败,其中输出显示 JoggerId = 0 和 UserId = null。

我没有在 asp.net 网站上的 Contoso 大学教程中看到这种行为,我也查看了 MSDN Learn EF 网站。我似乎无法解决这个问题。感谢您的建议。

【问题讨论】:

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


    【解决方案1】:

    经过反复试验,我找到了一种可以为外键分配值的方法。

    首先,我的关系设置不正确:UserProfile 类 不需要公共 int? GolferId 来表示一个导航属性。最终类如下所示:

    用户资料

      [Key]
      [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
      public int UserId { get; set; }
      public string UserName { get; set; }
    
      // Navigation properties
      public virtual Jogger Jogger { get; set; }
    

    慢跑课程

    [Key] 
    [ForeignKey("UserId")]
    public int JoggerId { get; set; }
    
    public string Pronation {get; set;}
    
    public virtual UserProfile UserId { get; set; }
    

    这无需任何 Fluent API 代码即可设置正确的关系。

    分配 ForeignKey 值有点棘手,我相信有比这更好的方法。

    1. 我使用了 FormCollection,而不是定义 (id = 0) 的其他脚手架方法。您还可以使用 [Bind(Include = "field1, field2, field3")] 确保发布正确的字段。
    2. 利用 User.Identity.Name 获得登录用户的 UserId 有很长的路要走
    3. FormCollection 允许我引用和保存其他字段,例如旋前

      [HttpPost]
      [ValidateAntiForgeryToken]
      public ActionResult Create(FormCollection values)
      {
      
          var jogger = new Jogger();
          TryUpdateModel(jogger);
      
          var context = new TestContext();
          var userqq = User.Identity.Name;
          var user = context.UserProfiles.SingleOrDefault(u => u.UserName == userqq);
      
          if (ModelState.IsValid)
          {
      
                  jogger.JoggerId = user.UserId;
                  jogger.Pronation = values["Pronation"];
      
      
                  db.Joggers.Add(jogger);
                  db.SaveChanges();
                 return View();
      
      
          }
              ViewBag.JoggerId = new SelectList(db.UserProfiles, "UserId", "UserName", jogger.JoggerId);
              return View(jogger);
      
      }
      

    这一点都不漂亮,但确实有效。感谢@Moeri 为我指明了正确的方向。

    【讨论】:

      【解决方案2】:

      错误在数据库模型中。 Entity Framework Code First 将始终需要配置一对一关系。 Code First 无法确定在这些情况下哪个类是依赖项。

      1- 在你的 Jogger 类中添加属性:

      public virtual User User { get; set; }
      

      2 - 在您的 OnModelCreating 方法中添加:

      protected override void OnModelCreating(DbModelBuilder modelBuilder)
      {
            // One to One relationship
            modelBuilder.Entity<User>()
                              .HasOptional(t => t.Jogger)
                              .WithOptionalDependent(m => m.User);
      }
      

      【讨论】:

      • 谢谢阿格多。我试图像这样构建它,但在 EF 查看器上出现错误。我设法找到了一种从 UserId 分配 JoggerId 的方法。
      【解决方案3】:

      这是你的错误:

      public virtual UserProfile UserId { get; set; }
      

      Entity Framework Code First 使用约定,其中之一是基于名称的约定。这意味着如果您有一个属性“UserId”,它将查找一个表“User”并将其配置为该表的外键。 我认为这很糟糕,因为您为导航属性指定了一个通常用于外键属性的名称。

      正确的工作方式是:

      public virtual int? UserId { get; set; }
      public virtual User User { get; set; }
      

      如果您希望您的属性被称为“UserProfileId”,则必须使用“ForeignKey”属性或流畅的配置语法让实体框架知道这是一个外键:

      public virtual int? UserProfileId{ get; set; }
      
      [ForeignKey("UserProfileId")
      public virtual User User { get; set; }
      

      或使用流畅的语法(您可以在 Context 的 OnModelCreating 方法中编写此代码,或者在继承自 EntityTypeConfiguration 的单独类中编写此代码):

      HasRequired(j => j.User).WithMany().HasForeignKey(i => i.UserProfileId)
      

      编辑:我刚刚注意到您的 User 表实际上称为 UserProfile,这使我的响应的后半部分变得毫无意义。不过我会保持原样,因为它可以具有教育意义。

      【讨论】:

      • 谢谢@Moeri 我使用公共虚拟UserProfile UserProfile 做了你在上半年建议的更改)但它仍然给了我同样的错误(模型有效)。添加失败时,JoggerId 仍设置为 0。
      • 我想知道我的解决方案是否存在根本性缺陷,因为即使我在 Jogger 表中手动输入 User Id 的数据库值,它仍然会杀死仅查看手动输入的 Jogger 记录的模型。跨度>
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-06-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-21
      相关资源
      最近更新 更多