c# with Rapporto opzionale one to one con l'API Fluent Framework di Entity




web api 2 dto (5)

Vogliamo utilizzare una relazione opzionale one to one utilizzando il codice di Entity Framework First. Abbiamo due entità.

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

    public int? LoyaltyUserDetailId { get; set; }
    public LoyaltyUserDetail LoyaltyUserDetail { get; set; }
}

public class LoyaltyUserDetail
{
    public int Id { get; set; }
    public double? AvailablePoints { get; set; }

    public int PIIUserId { get; set; }
    public PIIUser PIIUser { get; set; }
}

PIIUser può avere un LoyaltyUserDetail ma LoyaltyUserDetail deve avere un PIIUser . Abbiamo provato queste tecniche di approccio fluente.

modelBuilder.Entity<PIIUser>()
            .HasOptional(t => t.LoyaltyUserDetail)
            .WithOptionalPrincipal(t => t.PIIUser)
            .WillCascadeOnDelete(true);

Questo approccio non ha creato la chiave straniera PIIUsers nella tabella PIIUsers .

Successivamente abbiamo provato il seguente codice.

modelBuilder.Entity<LoyaltyUserDetail>()
            .HasRequired(t => t.PIIUser)
            .WithRequiredDependent(t => t.LoyaltyUserDetail);

Ma questa volta EF non ha creato nessuna chiave esterna in queste 2 tabelle.

Avete qualche idea per questo problema? Come possiamo creare una relazione opzionale one to one usando l'entity framework fluent api?


Ci sono molte cose che non vanno nel tuo codice.

Una relazione 1: 1 è: PK <-PK , dove un lato PK è anche un FK o PK <-FK + UC , dove il lato FK è un non-PK e ha un UC. Il tuo codice mostra che hai FK <-FK , poiché definisci entrambi i lati per avere un FK ma è sbagliato. PIIUser è il lato PK e LoyaltyUserDetail è il lato FK. Ciò significa che PIIUser non ha un campo FK, ma LoyaltyUserDetail fa.

Se la relazione 1: 1 è facoltativa, il lato FK deve avere almeno 1 campo nullable.

pswg sopra ha risposto alla tua domanda ma ha commesso un errore che ha anche definito un FK in PIIUser, che è ovviamente sbagliato come ho descritto sopra. Definire quindi il campo FK LoyaltyUserDetail in LoyaltyUserDetail , definire l'attributo in LoyaltyUserDetail per contrassegnarlo come campo FK, ma non specificare un campo FK in PIIUser .

Ottieni l'eccezione che descrivi sopra il post di pswg, perché nessun lato è il lato PK (principio di fine).

EF non è molto buono a 1: 1 in quanto non è in grado di gestire i vincoli univoci. Non sono esperto di codice prima, quindi non so se sia in grado di creare un UC o no.

(edit) btw: A 1: 1 B (FK) significa che c'è solo un vincolo FK creato, sul bersaglio di B che punta al PK di A, non 2.


Fai come se avessi una relazione uno-a-molti tra LoyaltyUserDetail e PIIUser quindi la mappatura deve essere

modelBuilder.Entity<LoyaltyUserDetail>()
       .HasRequired(m => m.PIIUser )
       .WithMany()
       .HasForeignKey(c => c.LoyaltyUserDetailId);

EF dovrebbe creare tutta la chiave estera di cui hai bisogno e non preoccuparti di WithMany !


Prova ad aggiungere l'attributo ForeignKey alla proprietà LoyaltyUserDetail :

public class PIIUser
{
    ...
    public int? LoyaltyUserDetailId { get; set; }
    [ForeignKey("LoyaltyUserDetailId")]
    public LoyaltyUserDetail LoyaltyUserDetail { get; set; }
    ...
}

E la proprietà PIIUser :

public class LoyaltyUserDetail
{
    ...
    public int PIIUserId { get; set; }
    [ForeignKey("PIIUserId")]
    public PIIUser PIIUser { get; set; }
    ...
}

L'unica cosa che confonde con le soluzioni precedenti è che la chiave primaria è definita come " Id" in entrambe le tabelle e se la chiave primaria basata sul nome della tabella non funziona, ho modificato le classi per illustrare lo stesso, cioè la tabella opzionale non dovrebbe definire la propria chiave primaria, dovrebbe invece usare lo stesso nome chiave dalla tabella principale.

public class PIIUser
{
    // For illustration purpose I have named the PK as PIIUserId instead of Id
    // public int Id { get; set; }
    public int PIIUserId { get; set; }

    public int? LoyaltyUserDetailId { get; set; }
    public LoyaltyUserDetail LoyaltyUserDetail { get; set; }
}

public class LoyaltyUserDetail
{
    // Note: You cannot define a new Primary key separately as it would create one to many relationship
    // public int LoyaltyUserDetailId { get; set; }

    // Instead you would reuse the PIIUserId from the primary table, and you can mark this as Primary Key as well as foreign key to PIIUser table
    public int PIIUserId { get; set; } 
    public double? AvailablePoints { get; set; }

    public int PIIUserId { get; set; }
    public PIIUser PIIUser { get; set; }
}

E poi seguito da

modelBuilder.Entity<PIIUser>()
.HasOptional(pi => pi.LoyaltyUserDetail)
.WithRequired(lu => lu.PIIUser);

Farebbe il trucco, la soluzione accettata non riesce a spiegare chiaramente questo, e mi ha buttato fuori per poche ore per trovare la causa


public class User
{
    public int Id { get; set; }
    public int? LoyaltyUserId { get; set; }
    public virtual LoyaltyUser LoyaltyUser { get; set; }
}

public class LoyaltyUser
{
    public int Id { get; set; }
    public virtual User MainUser { get; set; }
}

        modelBuilder.Entity<User>()
            .HasOptional(x => x.LoyaltyUser)
            .WithOptionalDependent(c => c.MainUser)
            .WillCascadeOnDelete(false);

questo risolverà il problema su REFERENCE e KEYS ESTERI

quando AGGIORNA o ELIMINA un record





ef-fluent-api