Double map the same entity

Asked

Viewed 142 times

5

I’m trying to map the same entity twice into 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 rotating the udpate-database I have the following error

Introducing FOREIGN KEY Constraint 'Fk_dbo. 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 always solved it using the 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 there’s any other way to solve this than by using these two ways?

The structure of the bank is as follows: inserir a descrição da imagem aqui

  • Why you need to map twice if you are an entity and you already have a Collection of them?

  • 'Cause it’s system business rule.

  • But should it be implemented in this layer? Precisely because it is a business rule?

  • And in what layer would it have to be if not in the entity?

  • If it is a business rule, it should not be in the entity, because its role is to reflect the structure of the bank, if you have an auxiliary table with two fks for the same table (Origin and Destination) you should have a specific Entity for it. But I’m just speculating, add the diagram of your data structure

  • But at what point would my entity not be reflecting the structure of the bank? I use Migrations by EF, my database structure is what is in the mapping of my entities.

Show 1 more comment

1 answer

2

There is a more "clean" way to map the class, you can use a Map class of type EntityTypeConfiguration from the EF Fluent API, in it you can specify everything at once related to the class that will be configured, in your case 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 website very good at it.

There is also a topical focused only on separating entity configurations into separate classes.

NOTE: I do not recommend using modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>(), as a future need for Cascade delete may arise which will make you have unnecessary maintenance in old classes.


Edited as requested by the OP in the comments:

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

You can configure on own model via Data Annotation by identifying [ForeignKey("IdForeignKey")] above the model related to this FK as you have already done.
To avoid Cascade delete without using the Fluent API the only solutions are the ones you have presented (my source for that statement and a font with all Data Annotations for you to prove), but you can set your intin the model itself for Nullable, that will make the Cascade delete be set to false by EF, but I can’t say if in your case this is the best solution, since if you don’t want null insertions you would have to implement some additional validations before the insertion command is executed.

In the case of the model presented 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; }
}
  • i would like to not configure this by Fluent API but by Data Annotations. Your example is well to say the same of mine where I use Onmodelcreating, only you separated into a file.

  • sorry it takes to reply friend, I will edit the reply with an alternative using Data Annotations

  • @Pablotondolodevargas edited

Browser other questions tagged

You are not signed in. Login or sign up in order to post.