Estoy tratando de asignar tablas de una base de datos IBM Db2 antigua a entidades que usan EntityFramework Core 2.2 pero estas tablas no tienen ninguna clave (sí, también me sorprendió) y no estoy seguro de si Incluso se me permite agregarlos a esta antigua bestia que ha estado funcionando desde la edad de piedra.

Hasta ahora he conseguido que las cosas funcionen mediante la ingeniería inversa de las relaciones y la búsqueda de posibles claves para que EF pueda rastrear felizmente estas entidades, pero estoy enfrentando un problema con una asignación de uno a muchos.

Quiero hacer una asignación de uno a muchos de la tabla Invoice a la tabla SellerAccountDetails. Mi configuracion:

Entidad de facturación:

public class FinvoiceConfiguration : IEntityTypeConfiguration<Finvoice>
{
    public void Configure(EntityTypeBuilder<Finvoice> builder)
    {
        // PK for Invoice
        builder.HasKey(f => new { f.FinvoiceBatchNumber, f.FinvoiceBatchRowNumber });

        // One-to-many to SellerAccountDetails table
        builder.HasMany(f => f.SellersAccountDetails)
            .WithOne(s => s.Invoice)
            .HasForeignKey(s => new
            {
                s.FinvoiceBatchNumber,
                s.FinvoiceBatchRowNumber,
                s.SellerAccountPartyIdentifier
            })
            .HasPrincipalKey(f => new
            {
                f.FinvoiceBatchNumber,
                f.FinvoiceBatchRowNumber,
                f.SellerPartyIdentifier
            });
    }
}

Entidad SellerAccountDetails:

public class SellerAccountDetailsConfiguration : IEntityTypeConfiguration<SellerAccountDetails>
{
    public void Configure(EntityTypeBuilder<SellerAccountDetails> builder)
    {
        // PK for SellerAccountDetails
        builder.HasKey(s => new
        {
            s.FinvoiceBatchNumber,
            s.FinvoiceBatchRowNumber,
            s.SellerAccountPartyIdentifier,
            s.SellerAccountID // <-- Needed for uniqueness of this entity
        });

        // Many-to-one to Invoice table
        builder.HasOne(s => s.Invoice)
            .WithMany(i => i.SellersAccountDetails)
            .HasForeignKey(s => new
            {
                s.FinvoiceBatchNumber,
                s.FinvoiceBatchRowNumber,
                s.SellerAccountPartyIdentifier
            })
            .HasPrincipalKey(f => new
            {
                f.FinvoiceBatchNumber,
                f.FinvoiceBatchRowNumber,
                f.SellerPartyIdentifier
            });
    }
}

Ahora esto funciona cuando hago una consulta y luego llamo ToList(), en mi código de prueba se supone que debo obtener 3 SellerAccountDetails entidades para una Finvoice, pero si no materializo IQueryable, obtengo 6 SellerAccountDetails por uno Finvoice, y si enumero de nuevo, aumentan a 9, y así sucesivamente ...

¿No debería EF saber por la clave primaria configurada en SellerAccountDetails qué hace que esta entidad sea única?

Agregué HasPrincipalKey porque la propiedad SellerPartyIdentifier (de Finvoice) corresponde a la propiedad SellerAccountPartyIdentifier en la tabla de cuentas, pero no es una clave principal. Si lo elimino y mantengo solo las 2 claves foráneas, el resultado es el mismo.

¿Cómo hago para que esto funcione? Puede que me falte algo obvio aquí, pero no lo veo.

Aquí está mi consulta de ejemplo:

var invoices = _dbContext.Invoices.Include(i => i.SellersAccountDetails).ThenInclude(s => s.Invoice).
                Where(i => i.FinvoiceBatchNumber == 13491).OrderBy(i => i.FinvoiceBatchRowNumber).Take(1);//.ToList();

¡Gracias!

0
Shahin Dohan 28 feb. 2020 a las 18:45

2 respuestas

La mejor respuesta

Resuelto! El problema era que los captadores de las propiedades de la entidad estaban recortando el campo:

private string _sellerAccountID;

[Column("FV77A", TypeName = "char(35)")]
[Required, DefaultValue("")]
[MinLength(0), MaxLength(35)]
public string SellerAccountID
{
    get { return _sellerAccountID.Trim(); } // <-- Trim() is the culprit
    set { _sellerAccountID = value; }
}

Por alguna razón, esto estaba confundiendo al rastreador EF, porque si también desactivo el rastreo usando AsNoTracking (o configurándolo globalmente haciendo optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);, entonces también funcionaría correctamente.

Eliminé el recorte (y los campos privados), e hice el recorte usando AutoMapper en el nivel DTO de esta manera: CreateMap<string, string>().ConvertUsing((s, d) => s.Trim());

Ahora siempre obtengo 3 resultados como debería, sin importar cuántas veces enumere IQueryable.

0
Shahin Dohan 2 mar. 2020 a las 10:48

No puede usar linq "Incluir" sin clave / relación en Db2.

Tienes que usar a continuación

var invoices = (from a in _dbContext.Invoices.Where(x => x.FinvoiceBatchNumber == 13491) 
                from b in _dbContext.SellersAccountDetails.Where(x => x.InvoiceId = a.Id).DefaultOrEmpty()
                select a, b).Take(1);
0
Asherguru 28 feb. 2020 a las 16:58