EF Core尝试插入具有现有主键的记录

时间:2019-12-15 11:15:50

标签: c# sql-server .net-core entity-framework-core

EF Core引发异常:

  

System.InvalidOperationException:'无法跟踪实体类型'Appointment'的实例,因为已经跟踪了另一个键值为'{Id:6}'的实例。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。'

即使主键= 0,也要插入模型时。

Appointment.cs

public class Appointment
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [Key]
    public int Id { get; set; }
    public Doctor Doctor { get; set; }
    public Patient Patient { get; set; }
    public DateTime DateTime { get; set; }

    public Appointment()
    {
    }

    public Appointment(Doctor doctor, Patient patient)
    {
        this.Doctor = doctor;
        this.Patient = patient;
        this.DateTime = DateTime.Now;
    }

    public Appointment(Doctor doctor, Patient patient, DateTime dateTime)
    {
        this.Doctor = doctor;
        this.Patient = patient;
        this.DateTime = dateTime;
    }
}

我如何创建约会实例:

internal async Task AddAppointmentAsync(int doctorId, int patientId, DateTime dateTime)
{
    Doctor doctor = null;
    Patient patient = null;

    //Retrieve doctor from the db
    using (var doctorController = new DoctorController())
        await Task.Run(() => doctor = doctorController.GetDoctor(doctorId));
    if (doctor == null)
        throw new KeyNotFoundException("Doctor with the specified id doesn't exist.");

    //Retrieve patient from the db
    using (var patientController = new PatientController())
        await Task.Run(() => patient = patientController.GetPatient(patientId));
    if (patient == null)
        throw new KeyNotFoundException("Patient with the specified id doesn't exist.");

    //Create and insert appointment into the db
    Appointment appointment = new Appointment(doctor, patient, dateTime);
    using (_controller = new AppointmentController())
       await Task.Run(() => _controller.AddAppointment(appointment));
}

添加约会方式

public void AddAppointment(Appointment appointment)
{
    if (appointment == null) throw new ArgumentNullException(nameof(appointment));
    if (appointment.Doctor == null) throw new ArgumentNullException(nameof(appointment.Doctor));
    if (appointment.Patient == null) throw new ArgumentNullException(nameof(appointment.Patient));

    Doctor doctor = appointment.Doctor;
    Patient patient = appointment.Patient;
    appointment.Doctor = null;
    appointment.Patient = null;

    this._context.Appointments.Add(appointment); 

    appointment.Doctor = doctor;
    appointment.Patient = patient;

    this._context.SaveChanges(); //Exception is thrown here
}

我的环境:

public class HospitalContext : DbContext
{
    private const string _connectionString = "Server=(localdb)\\mssqllocaldb;Database=ClientDb;Trusted_Connection=True;";

    public DbSet<Doctor> Doctors { get; private set; }
    public DbSet<Patient> Patients { get; private set; }
    public DbSet<Appointment> Appointments { get; private set; }
    public DbSet<MedicalRecord> MedicalRecords { get; private set; }

    public HospitalContext()
        : base()
    {
    }

    public HospitalContext(DbContextOptions<HospitalContext> options)
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Doctor>()
            .HasMany(d => d.Appointments)
            .WithOne(a => a.Doctor)
            .OnDelete(DeleteBehavior.Cascade);

        modelBuilder.Entity<Doctor>()
            .HasMany(w => w.WorkDays)
            .WithOne(a => a.Doctor)
            .OnDelete(DeleteBehavior.Cascade);

        modelBuilder.Entity<Patient>()
            .HasMany(p => p.Appointments)
            .WithOne(p => p.Patient)
            .OnDelete(DeleteBehavior.Cascade);

        modelBuilder.Entity<Patient>()
            .HasMany(p => p.MedicalRecords)
            .WithOne(m => m.Patient)
            .OnDelete(DeleteBehavior.Cascade);
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (!optionsBuilder.IsConfigured)
        {
            optionsBuilder.UseSqlServer(_connectionString);
            optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
            optionsBuilder.EnableSensitiveDataLogging();

        }
    }
}

我要传递给该方法的内容:

enter image description here

我的约会表中已经有什么:

enter image description here

更新

看来我的表不能包含具有相同DoctorIdPatientId外键的多个条目。 例如:尝试添加DoctorId = 3,PatientId = 1的条目时引发异常:

enter image description here

2 个答案:

答案 0 :(得分:1)

如果您尝试插入的实体在数据库中已经有一个实体的ID, 那么很可能是数据库中已经存在的实体。

也许您会做类似的事情:

  var item = YourCollection.First();

  item.ID = 0;
  item.DateTime = DateTime.Now


  YourCollection.Add(item);

这行不通,并且会产生您的行为。您必须使用new Appointment()

物理创建一个新项目

如果您插入两次,也会发生同样的情况

  YourCollection.Add(item);
  YourCollection.Add(item);

这不是您的情况,因为您已分配了现有商品的ID。

按ID跟踪只是实体框架的一部分,但它也通过引用跟踪实体。

答案 1 :(得分:0)

在将实体的State分配到Added之前,将其添加到上下文中可以解决此问题。

添加约会方式

public void AddAppointment(Appointment appointment)
{
    if (appointment == null) throw new ArgumentNullException(nameof(appointment));
    if (appointment.Doctor == null) throw new ArgumentNullException(nameof(appointment.Doctor));
    if (appointment.Patient == null) throw new ArgumentNullException(nameof(appointment.Patient));

    this._context.Entry(appointment).State = EntityState.Added;

    this._context.Appointments.Add(appointment);
    this._context.SaveChanges();
}