c# - with - web api 2 dto




Rapporto opzionale one to one con l'API Fluent Framework di Entity (4)

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.


Codice EF Prima supporta le relazioni 1:1 e 1:0..1 . Quest'ultimo è quello che stai cercando ("uno a zero-o-uno").

I tuoi tentativi di parlare fluentemente sono richiesti in entrambe le estremità in un caso e opzionali su entrambe le estremità nell'altro.

Quello che ti serve è facoltativo da un lato e richiesto dall'altro.

Ecco un esempio del primo libro del codice EF di programmazione

modelBuilder.Entity<PersonPhoto>()
.HasRequired(p => p.PhotoOf)
.WithOptional(p => p.Photo);

L'entità PersonPhoto ha una proprietà di navigazione denominata PhotoOf che punta a un tipo Person . Il tipo Person ha una proprietà di navigazione chiamata Photo che punta al tipo PersonPhoto .

Nelle due classi correlate, si utilizza la chiave primaria di ciascun tipo, non le chiavi esterne . cioè, non utilizzerai le proprietà LoyaltyUserDetailId o PIIUserId . Invece, la relazione dipende dai campi Id di entrambi i tipi.

Se si sta utilizzando l'API fluente come sopra, non è necessario specificare LoyaltyUser.Id come chiave esterna, EF lo individuerà.

Quindi, senza avere il tuo codice per mettermi alla prova (odio farlo dalla mia testa) ... lo tradurrei nel tuo codice come

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

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

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
  modelBuilder.Entity<LoyaltyUserDetail>()
  .HasRequired(lu => lu.PIIUser )
  .WithOptional(pi => pi.LoyaltyUserDetail );
}

La proprietà LoyaltyUserDetails PIIUser è richiesta e la proprietà LoyaltyUserDetail di LoyaltyUserDetail è facoltativa.

Potresti iniziare dall'altra parte:

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

che ora dice che la proprietà LoyaltyUserDetail di LoyaltyUserDetail è facoltativa e che è richiesta la proprietà PIIUser di PIIUser .

Devi sempre usare il modello HAS / WITH.

Le relazioni HTH e FWIW, uno a uno (o uno a zero / uno) sono una delle relazioni più confuse da configurare prima nel codice in modo da non essere soli! :)


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


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; }
    ...
}






ef-fluent-api