Entity Framework 与 ASP.NET 一对多关系导致 NullReference 错误
Entity Framework with ASP.NET one-to-many relation causes NullReference error
在我的数据库中有两个实体:DbStatus 和 DbTask
public class DbStatus
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<DbTask> Tasks { get; set; }
}
public class DbTask
{
public int Id { get; set; }
public string Title { get; set; }
public bool Done { get; set; }
public int StatusId { get; set; }
public virtual DbStatus Status { get; set; }
}
在OnModelCreating
方法中,我用下面的代码建立关系:
modelBuilder.Entity<DbStatus>()
.HasMany(s => s.Tasks)
.WithOne(t => t.Status)
.HasForeignKey(t => t.StatusId);
我也在这个方法中添加了一些示例数据,设置新创建的DbTasks
的StatusId
。
问题是,当我尝试使用
访问 DbTask
的 Status
名称时
task.Status.Name
我得到一个NullReferenceException
。
谁能帮我正确设置关系?
重要
对于阅读本文的任何人,Rob 提供了最快的解决方案(以及满足特定任务标准的解决方案)。但是,您应该阅读并实施 Steve Py 提供的解决方案,原因在他们的回答中也有所描述!
从数据库中获取 DbTasks
的列表时,您需要告诉它包含子 Status
对象。
尝试这样的事情:
var tasks = dbContext.DbTasks
.Include(t => t.Status)
.ToList();
在实体上设置 FK 不会自动导致加载相关实体。使用导航属性时,我建议避免在实体中声明 FK 字段并使用影子属性来避免此类问题。
要更新 DbTask 的状态:
public ActionResult MarkTaskComplete(int taskId)
{
var completeStatus = _context.Statuses.Single(x => x.StatusId = Statuses.Complete);
// TODO: Validation that user can update task etc.
var task = _context.Tasks
.Include(x => x.Status)
.Single(x => x.TaskId == taskId);
task.Status = completeStatus;
_context.SaveChanges();
return Json(new { success = true; status = task.Status.Name } );
}
FK 字段的问题是行为可能会有所不同,具体取决于您是使用导航 属性 还是 FK,以及导航 属性 是否预先加载。从任务的角度来看,当前状态有两个真实来源,一些代码可能会检查 task.StatusId,而另一些代码会使用 task.Status.StatusId。这些值可能会有所不同,具体取决于在没有更新另一个的情况下更新一个值。
虽然这可能意味着访问数据库以获取状态,但通过 ID 获取行非常快,并且还可以验证您的方法是否仅使用合法值。
在我的数据库中有两个实体:DbStatus 和 DbTask
public class DbStatus
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<DbTask> Tasks { get; set; }
}
public class DbTask
{
public int Id { get; set; }
public string Title { get; set; }
public bool Done { get; set; }
public int StatusId { get; set; }
public virtual DbStatus Status { get; set; }
}
在OnModelCreating
方法中,我用下面的代码建立关系:
modelBuilder.Entity<DbStatus>()
.HasMany(s => s.Tasks)
.WithOne(t => t.Status)
.HasForeignKey(t => t.StatusId);
我也在这个方法中添加了一些示例数据,设置新创建的DbTasks
的StatusId
。
问题是,当我尝试使用
访问DbTask
的 Status
名称时
task.Status.Name
我得到一个NullReferenceException
。
谁能帮我正确设置关系?
重要
对于阅读本文的任何人,Rob 提供了最快的解决方案(以及满足特定任务标准的解决方案)。但是,您应该阅读并实施 Steve Py 提供的解决方案,原因在他们的回答中也有所描述!
从数据库中获取 DbTasks
的列表时,您需要告诉它包含子 Status
对象。
尝试这样的事情:
var tasks = dbContext.DbTasks
.Include(t => t.Status)
.ToList();
在实体上设置 FK 不会自动导致加载相关实体。使用导航属性时,我建议避免在实体中声明 FK 字段并使用影子属性来避免此类问题。
要更新 DbTask 的状态:
public ActionResult MarkTaskComplete(int taskId)
{
var completeStatus = _context.Statuses.Single(x => x.StatusId = Statuses.Complete);
// TODO: Validation that user can update task etc.
var task = _context.Tasks
.Include(x => x.Status)
.Single(x => x.TaskId == taskId);
task.Status = completeStatus;
_context.SaveChanges();
return Json(new { success = true; status = task.Status.Name } );
}
FK 字段的问题是行为可能会有所不同,具体取决于您是使用导航 属性 还是 FK,以及导航 属性 是否预先加载。从任务的角度来看,当前状态有两个真实来源,一些代码可能会检查 task.StatusId,而另一些代码会使用 task.Status.StatusId。这些值可能会有所不同,具体取决于在没有更新另一个的情况下更新一个值。
虽然这可能意味着访问数据库以获取状态,但通过 ID 获取行非常快,并且还可以验证您的方法是否仅使用合法值。