【问题标题】:Entity Framework - Attach entity Error when creating Parent Entity实体框架 - 创建父实体时附加实体错误
【发布时间】:2019-03-13 12:49:03
【问题描述】:

我收到以下错误

“附加“Datos.Medico”类型的实体失败,因为相同类型的另一个实体已经具有相同的主键值。当使用“附加”方法或将实体的状态设置为“未更改”时,可能会发生这种情况' 或 'Modified' 如果图中的任何实体具有冲突的键值。这可能是因为某些实体是新的并且尚未收到数据库生成的键值。在这种情况下,请使用 'Add' 方法或 'Added' 实体state 来跟踪图表,然后根据需要将非新实体的状态设置为“未更改”或“已修改”。”

当我尝试将实体附加到上下文时发生错误,如下所示:

public override void Alta(Medico medico)
{
    foreach (Especialidad especialidad in medico.Especialidad)
    {
        this.context.Especialidad.Attach(especialidad);
    }
    base.Alta(medico);
}

Especialidad 实体被获取为不跟踪,因为它用于填充组合:

public List<Especialidad> ObtenerEspecialidades()
{
    var especialidades = context.Especialidad.AsNoTracking();
    /*
    var especialidades = from unaEspecialidad in context.Especialidad
                         select unaEspecialidad;
                         */

    return especialidades.ToList();
}

然后从组合中取出并添加到包含List&lt;Especialidad&gt;Medico 实体。

提前致谢。

更新

我按要求包括实体定义:

    public Medico()
    {
        this.Agenda = new HashSet<Agenda>();
        this.Historia_Clinica_Elemento = new HashSet<HistoriaClinicaElemento>();
        this.Turno = new HashSet<Turno>();
        this.Especialidad = new HashSet<Especialidad>();
        this.Espera_Atencion = new HashSet<EsperaAtencion>();
    }

    public int ID { get; set; }
    public string Nombre { get; set; }
    public string Apellido { get; set; }
    public Nullable<int> Matricula { get; set; }
    public Nullable<int> TipoDocumento { get; set; }
    public Nullable<long> Numero_Documento { get; set; }
    public Nullable<System.DateTime> Fecha_Nacimiento { get; set; }
    public Nullable<int> Direccion { get; set; }
    public string Usuario { get; set; }

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<Agenda> Agenda { get; set; }
    public virtual Direccion Direccion1 { get; set; }
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<HistoriaClinicaElemento> Historia_Clinica_Elemento { get; set; }
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<Turno> Turno { get; set; }
    public virtual TipoDocumento Tipo_Documento { get; set; }
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<Especialidad> Especialidad { get; set; }
    public virtual Usuario Usuario1 { get; set; }
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<EsperaAtencion> Espera_Atencion { get; set; }

特别:

public partial class Especialidad
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
    public Especialidad()
    {
        this.Medico = new HashSet<Medico>();
    }

    public int ID { get; set; }
    public string Nombre { get; set; }

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<Medico> Medico { get; set; }
}

【问题讨论】:

    标签: c# entity-framework


    【解决方案1】:

    根据您的 DbContext 保持打开状态的时间长短,它可能已经与实体相关联。例如,如果您正在遍历一组 Medico,并且其中两个关联了相同的 Espacialidad,则第一个将附加,而第二个会引发错误。

    在将实体附加到 DbContext 之前,您应该始终检查实体是否已附加:

    foreach (Especialidad especialidad in medico.Especialidad)
    {
         if (!this.context.Especialidad.Local.Any(x => x == especialidad))        
             this.context.Especialidad.Attach(especialidad);
    }
    

    编辑:对于没有双向引用的多对多。

    从 Especialidad 中删除集合 Medico

    您目前将医生映射到的位置如下:

    HasMany(x => x.Especialidad)
        .WithMany(x => x.Medico)
        .Map(x => 
        { 
           x.ToTable("Medico_Especialidad");
           x.MapLeftKey("MedicoID");
           x.MapRightKey("EspecialidadID");
        });
    

    ...这将变为:

    HasMany(x => x.Especialidad)
        .WithMany() // Note the empty return reference.
        .Map(x => 
        { 
           x.ToTable("Medico_Especialidad");
           x.MapLeftKey("MedicoID");
           x.MapRightKey("EspecialidadID");
        });
    

    您可能需要按专业获取可能正在使用 Especialidad.Medico 的医生的任何代码都需要更改以从医生方面解决,即

    代替:

    var medicos = context.Especialidad.Find(especialidadID).Medico

    看起来像:

    var medicos = context.Medico.Where(x =&gt; x.Especialidad.Any(e =&gt; e.ID == especialidadID)).ToList();

    当涉及到多对多或多对一关系时,为了使代码更简单并避免引用混淆,通常建议避免双向引用,除非绝对需要它们。双向引用带来的部分麻烦是,当 A 引用 B,而 B 引用 A 时,您需要确保上下文解析双方完全相同的 A 和 B。在将实体分离和重新附加到上下文时,这将导致关联错误或重复的 PK 插入错误。

    【讨论】:

    • 我添加了这个检查,似乎实体不在上下文中。尝试添加时同样的错误。
    • 检查实体之间的引用。该错误与附加 Medico 有关,因此如果 Especialidad 保留对 Medico 的引用,则应检查该引用是否与 DbContext 知道的实例相同。分离和重新附加实体很容易变得混乱。您可能需要根据本地人的 ID 检查 Medico 和 Especialidads,因为它看起来像是您尝试附加的一个或多个实体的本地副本。
    • 其实没有附加实体“Medico”。此错误是在“Create Medico”方法中生成的。所以,目前,“medico”根本不应该存在。
    • 您能否更新您的代码示例以包含 Medico 和 Especialidad 的实体定义,以及引发异常的方法。
    • 谢谢。好的,那么 Medico(医生)和 Especialidad(专科)之间的关系是多对多的吗?会有一个像 MedicoEspecialidad 这样的连接表吗?通常对于这样的关系,我会将 Specialty 视为一种查找,其关系映射在 Doctor 的单个方向上。 (删除 Especialidad 中的 Medico 集合。)它仍然是多对多但没有双向引用。使用断点在异常之前查看该专业是否引用了其他医生。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-02-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-13
    • 1970-01-01
    相关资源
    最近更新 更多