6

我正在使用 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 站点。我似乎无法解决这个问题。感谢您的建议。

4

3 回答 3

3

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

首先,我的关系设置不正确: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. 我没有使用其他定义 (id = 0) 的脚手架方法,而是使用了 FormCollection。您还可以使用 [Bind(Include = "field1, field2, field3")] 来确保发布正确的字段。
  2. 使用 User.Identity.Name 获取登录用户的 UserId 有很长的路要走
  3. FormCollection 允许我参考和保存其他字段,例如 Pronation

    [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 为我指明了正确的方向。

于 2013-08-06T09:58:42.673 回答
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);
}
于 2013-08-03T03:34:13.283 回答
1

这是您的错误:

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,这使我的响应的后半部分变得毫无意义。不过,我会保持原样,因为它可以具有教育意义。

于 2013-08-02T09:08:46.720 回答