c# – Fluent NxN Class Mapping with Composite Key

Question:

I have two classes that have composite primary keys, for example:

Cliente :

public class Cliente
{
    public int EscritorioId { get; set; }
    public virtual Escritorio Escritorio { get; set; }

    public int Id { get; set; }
}

public class ClienteMapping : EntityTypeConfiguration<Cliente>
{
    public ClienteMapping()
    {
        HasKey(x => new { x.Id, x.EscritorioId });
    }
}

Documento :

public class Documento
{
    public int EscritorioId { get; set; }
    public virtual Escritorio Escritorio { get; set; }

    public int Id { get; set; }

    public virtual ICollection<Cliente> ClientesComAcesso { get; set; }
}

And in the Documento class mapping I have an NxN mapping:

public class DocumentoMapping: EntityTypeConfiguration<Documento>
{
    public DocumentoMapping()
    {
        HasKey(x => new { x.EscritorioId, x.Id });

        HasMany(x => x.Clientes)
            .WithMany()
            .Map(m => 
            {
                x.MapLeftKey("EscritorioId", "DocumentoId");
                x.MapLeftKey("EscritorioId", "ClienteId");
                x.ToTable("DocumentoClientes");
            });
    }
}

Because a customer can be recipient/have access to several documents.

However, when trying to generate the Migrations file for this case I get the following error:

The specified association foreign key columns 'DocumentId' are invalid. The number of columns specified must match the number of primary key columns.

Trying to give another name to the composite keys, such as Documento_EscritorioId and Cliente_EscritorioId , for example:

HasMany(x => x.Clientes)
    .WithMany()
    .Map(m => 
    {
        x.MapLeftKey("Documento_EscritorioId", "DocumentoId");
        x.MapLeftKey("Cliente_EscritorioId", "ClienteId");
        x.ToTable("DocumentoClientes");
    });

It works, but in this case we have a redundancy.

What is the correct way to do this mapping?

Answer:

I don't see the need to use EntityTypeConfiguration for something so simple. Models can look like this:

public class Cliente
{
    [Key]
    public int Id { get; set; }
    public int EscritorioId { get; set; }

    public virtual Escritorio Escritorio { get; set; }
    public virtual ICollection<DocumentoCliente> DocumentosComAcesso { get; set; }
}

public class Documento
{
    public int Id { get; set; }
    public int EscritorioId { get; set; }

    public virtual Escritorio Escritorio { get; set; }    
    public virtual ICollection<DocumentoCliente> ClientesComAcesso { get; set; }
}

I find it more interesting to create the associative entity, even if it initially seems wordy. The gain in development is greater, because you can add more fields inherent to the association itself:

public class DocumentoCliente 
{
    [Key]
    public int DocumentoClienteId { get; set; }
    [Index("IUQ_DocumentoCliente_DocumentoId_ClienteId", IsUnique = true, Order = 1)]
    public int DocumentoId { get; set; }
    [Index("IUQ_DocumentoCliente_DocumentoId_ClienteId", IsUnique = true, Order = 2)]
    public int ClienteId { get; set; }

    public virtual Documento Documento { get; set; }
    public virtual Cliente Cliente { get; set; }
}

[Index] , introduced in this form from Entity Framework 6.1.0, guarantees the uniqueness of the associative record. Additional validations may be required in the application to avoid strange key duplication errors for the user.

Scroll to Top