Map the same entity twice

4

I'm trying to map the same entity twice in another

public class Conveniado
{
    public int Id { get; set; }

    public string Nome { get; set; }

    [InverseProperty(nameof(ProcedimentoAgregado.ConveniadoDe))]
    public virtual ICollection<ProcedimentoAgregado> ProcedimentoAgregadoConveniadosDe { get; set; }

    [InverseProperty(nameof(ProcedimentoAgregado.ConveniadoPara))]
    public virtual ICollection<ProcedimentoAgregado> ProcedimentoAgregadoConveniadosPara { get; set; }
}

public class Procedimento
{
    public int Id { get; set; }

    public string Descricao { get; set; }

    [InverseProperty(nameof(ProcedimentoAgregado.ProcedimentoDe))]
    public virtual ICollection<ProcedimentoAgregado> ProcedimentoAgregadoProcedimentosDe { get; set; }

    [InverseProperty(nameof(ProcedimentoAgregado.ProcedimentoPara))]
    public virtual ICollection<ProcedimentoAgregado> ProcedimentoAgregadoProcedimentosPara { get; set; }
}

public class ProcedimentoAgregado
{
    [Key]
    public Guid ProcedimentoAgregadoId { get; set; }

    [Display(Name = "Conveniado de")]
    public int ConveniadoDeId { get; set; }

    [Display(Name = "Procedimento de")]
    public int ProcedimentoDeId { get; set; }

    [Display(Name = "Conveniado para")]
    public int ConveniadoParaId { get; set; }

    [Display(Name = "Procedimento para")]
    public int ProcedimentoParaId { get; set; }

    [ForeignKey(nameof(ConveniadoDeId))]
    public virtual Conveniado ConveniadoDe { get; set; }

    [ForeignKey(nameof(ConveniadoParaId))]
    public virtual Conveniado ConveniadoPara { get; set; }

    [ForeignKey(nameof(ProcedimentoDeId))]
    public virtual Procedimento ProcedimentoDe { get; set; }

    [ForeignKey(nameof(ProcedimentoParaId))]
    public virtual Procedimento ProcedimentoPara { get; set; }
}

When running udpate-database I get the following error

  

Introducing FOREIGN KEY constraint 'FK_dbo.ProcedureAdded_Dbo.Procedure_ProceduresForId' on table 'ProcedureOgregates' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.   Could not create constraint or index. See previous errors.

I've always solved this using OnModelCreating of the context

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<ProcedimentoAgregado>()
        .HasRequired(a => a.ProcedimentoPara)
        .WithMany(a => a.ProcedimentoAgregadoProcedimentosPara)
        .HasForeignKey(a => a.ProcedimentoParaId)
        .WillCascadeOnDelete(false);

    modelBuilder.Entity<ProcedimentoAgregado>()
        .HasRequired(a => a.ConveniadoPara)
        .WithMany(a => a.ProcedimentoAgregadoConveniadosPara)
        .HasForeignKey(a => a.ConveniadoParaId)
        .WillCascadeOnDelete(false);

    base.OnModelCreating(modelBuilder);
}

Or

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

    base.OnModelCreating(modelBuilder);
}

I wonder if you have any other way to solve this without using these two ways?

The structure of the bank is as follows

    
asked by anonymous 29.09.2018 / 01:14

1 answer

1

There is a cleaner way to map the class, you can use a Map class of type EntityTypeConfiguration of Fluent API of EF, in it you can specify everything at once related to the class that will be configured in your if I believe it would look like this:

public class ProcedimentoAgregadoEntityConfiguration: EntityTypeConfiguration<ProcedimentoAgregado>
{
    public ProcedimentoAgregadoEntityConfiguration()
    {
            this.ToTable("ProcedimentoAgregadoes");

            this.HasKey<Guid>(s => s.ProcedimentoAgregadoId);

            this.HasMany(x => x.ConveniadoPara).HasForeignKey(x => x.ConveniadoParaId).WillCascadeOnDelete(false);

            this.HasMany(x => x.ProcedimentoPara).HasForeignKey(x => x.ProcedimentoParaId).WillCascadeOnDelete(false);

           //também defina as propriedades required para evitar erros

    }
}

And then in your model builders you can put:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Configurations.Add(new ProcedimentoAgregadoEntityConfiguration());
/...
}

If you want to know how to use and what is Fluent API there is a site very good for that.

There is also a topic focused only on separating entity settings into separate classes.

NOTE: I do not recommend using modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>() , as there may be a future need for cascade delete that will cause you to have unneeded maintenance on old classes.

Edited as PO request in comments:

  

I would like to not configure this by Fluent API and yes by Data Annotations.

You can configure your own model via Data Annotation via the [ForeignKey("IdForeignKey")] identifier above the model related to that FK as you already did. To avoid cascade delete without using the Fluent API the only solutions are the ones you have presented ( my source for this claim and a source with all Date Annotations for you to verify ), however you can set your int in the model itself to Nullable , this will cause cascade delete to be set to false by EF, but not I can say if this is the best solution in your case, since if you do not want null insertions you would have to implement some additional validations before the insert command is executed.

In the case of the model presented it would look like this:

public class ProcedimentoAgregado
{
    [Key]
    public Guid ProcedimentoAgregadoId { get; set; }

    [Display(Name = "Conveniado de")]
    public int? ConveniadoDeId { get; set; }

    [Display(Name = "Procedimento de")]
    public int? ProcedimentoDeId { get; set; }

    [Display(Name = "Conveniado para")]
    public int? ConveniadoParaId { get; set; }

    [Display(Name = "Procedimento para")]
    public int? ProcedimentoParaId { get; set; }

    [ForeignKey(nameof(ConveniadoDeId))]
    public virtual Conveniado ConveniadoDe { get; set; }

    [ForeignKey(nameof(ConveniadoParaId))]
    public virtual Conveniado ConveniadoPara { get; set; }

    [ForeignKey(nameof(ProcedimentoDeId))]
    public virtual Procedimento ProcedimentoDe { get; set; }

    [ForeignKey(nameof(ProcedimentoParaId))]
    public virtual Procedimento ProcedimentoPara { get; set; }
}
    
29.09.2018 / 16:25