sql - 在一对多关系中初始化子实体会导致重复
问题描述
我遇到了那个问题。我的问题很相似。
我有两个 EF 课程 Appointment 和 Job。它们之间具有一对多的关系。
public class Appointment
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int AppointmentID { get; set; }
public int AppointmentStatus { get; set; }
public string Remarks { get; set; }
public ICollection<Job> Job {get; set; }
}
public class Job
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int JobId { get; set; }
public string JobName { get; set; }
public string JobDescription { get; set; }
public short JobMode { get; set; }
public virtual Appointment Appointments { get; set; }
}
当我尝试将作业添加到 Appointment 实例时,我收到 nullreferenceexception。因为 Appointment 的 Job 属性返回 null。到现在都正常。
为了处理该异常,我尝试在 Parent 的构造中添加一个块,以便在创建约会时初始化新的 Job 项。
public Appointment()
{
if (this.Job == null)
{
this.Job = new Collection<Job>();
}
}
那时,我不再收到 nullreferenceexception,但该解决方案会导致重复的 Job 项。新约会成功创建但(!),不是我选择的工作。实际上是我选择的 Job 的一个实例。每次创建一个新的工作实例并与新创建的约会相关联。
解决方案
我建议坚持实体框架代码优先约定。它使您和将来阅读您的代码的人的生活更轻松。
在您的情况下,这意味着 everyAppointment
有零个或多个Jobs
,并且each使用外键Job
完全属于一个。Appointment
class Appointment
{
public int Id {get; set;}
// every Appointment has zero or more Jobs:
public virtual ICollection<Job> Jobs {get; set;}
...
}
class Job
{
public int Id {get; set;}
// every Job belongs to exactly one Appointment using foreign key:
public int AppointmentId {get; set;}
public virtual Appointment Appointment {get; set;}
...
}
因为我坚持约定,实体框架能够检测到一对多的关系。它检测主键和外键:不需要属性也不需要流畅的 API。
在 Entity Framework 中,表中的列是类中的非虚拟属性。表之间的关系(一对多,多对多,...)被标记为虚拟:它们不是您表中的真实项目
这只是一个建议,如果您有充分的理由偏离约定,您当然可以这样做。
但是,必须将表之间的关系定义为虚拟的。您还需要定义外键。
完成此操作后,您获取的约会将不会有空作业:
var fetchedAppointment = dbContext.Appointments
.Where(appointment => appointment.Id = ...)
.FirstOrDefault();
// fetchedAppointment.Jobs is not null!
// add a new Job:
fetchedAppointment.Jobs.Add(new Job()
{
// no need to fill the Id, nor the foreign key. Entity Framework will do that for you
JobName = ...,
JobDescription = ...,
...
});
dbContext.SaveChanges();
但是,如果您想添加一个或多个职位的新约会,您必须自己添加它们。可以使用数组或者列表,没关系,只要实现ICollection<Job>
var addedAppointment = dbContext.Appointments.Add(new Appointment()
{
// no need to fill the Id: entity framework will do that for you
AppointmentStatus = ...
Remarks = ...
Jobs = new List<Job>()
{
// again: no need to fill the Id, nor the foreign key.
// Entity Framework will do that for you
new Job()
{
JobName = ...,
JobDescription = ...,
},
new Job()
{
JobName = ...,
JobDescription = ...,
},
...
},
});
// if you want, you can also add a Job here:
addedAppointment.Jobs.Add(new Job()
{
JobName = ...,
JobDescription = ...,
});
// All primary and foreign keys will be filled as soon as you call SaveChanges
dbContext.SaveChanges();
当然,您也可以将 Job 添加到具有它所属约会的上下文中:
var addedJob = dbContext.Jobs.Add(new Job()
{
JobName = ...,
JobDescription = ...,
// this Job is a job of addedAppointment
Appointment = addedAppointment,
}
或者如果您已经保存了约会,那么主键有一个值:
var addedJob = dbContext.Jobs.Add(new Job()
{
JobName = ...,
JobDescription = ...,
// fill the foreign key instead
AppointmentId = addedAppointment.Id
},
推荐阅读
- c - 使用 MPI_Alltoallv 转置矩阵
- r - 在 GTSummary 中对行进行分组
- c# - CommandLineParser 是否支持可测试性功能?
- python - 如何运行ai
我正在尝试实现一个“i 不等于 j”(
i<j
) 循环,它会跳过 的情况i = j
,但我还想进一步要求循环不重复 的排列(j,i)
,如果- python - 告诉那个进程不在后台?
- java - Java System.out.println 的 PHP 等价物是什么
- java - Github API:从同一组织下的现有仓库创建新仓库
- linux - :不是一个有效的标识符 bash 出口说
- angular - Angular:使用搜索选择下拉(Mat-Select)并填写数据库。初始值问题
- javascript - 如何让 svg 图像仅在 div 标签内完全移动?