Ho un User
modello:
public class User
{
[Key]
public int IDUser { get; set; }
[Required]
public string Forename { get; set; }
[Required]
public string Name { get; set; }
public int? IDUser_CreatedBy { get; set; }
public User User_CreatedBy { get; set; }
}
L'utente può avere il suo creatore ( User_CreatedBy
) con il suo ID ( IDUser_CreatedBy
) che risiede nella stessa tabella, ovviamente, ma voglio avere l'opportunità di lasciarlo come valore nullo ( User
con creatore sconosciuto). Ecco perché IDUser_CreatedBy
ha int?
tipo nullable.
Non so come impostare il mio API fluente allo scopo di impegnare che chiave esterna IDUser_CreatedBy
alla chiave primaria IDUser
nella stessa tabella.
So che se elimino IDUser_CreatedBy
chiave esterna da quel modello e aggiungo nuova migrazione, allora il core EF creerà implicitamente la chiave esterna della proprietà shadow ma desidero avere l'opportunità di aggiornare il mio User
secondo momento (nel controller MVC) con la colonna creata dall'utente IDUser_CreatedBy
ed essere in grado di nominare quella colonna per me stesso. Inoltre non voglio la proprietà shadow dato che non ho alcun controllo sul nominare quella colonna.
Come posso ottenerlo?
MODIFICARE
@ IvanVtoev - grazie per la tua risposta ma il tuo esempio non produce per me un codice di migrazione valido:
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Users_Users_CreatedByIDUser",
table: "Users");
migrationBuilder.RenameColumn(
name: "CreatedByIDUser",
table: "Users",
newName: "UserIDUser");
migrationBuilder.RenameIndex(
name: "IX_Users_CreatedByIDUser",
table: "Users",
newName: "IX_Users_UserIDUser");
migrationBuilder.AddColumn<int>(
name: "IDUser_CreatedBy",
table: "Users",
nullable: true);
migrationBuilder.CreateIndex(
name: "IX_Users_IDUser_CreatedBy",
table: "Users",
column: "IDUser_CreatedBy");
migrationBuilder.AddForeignKey(
name: "FK_Users_Users_IDUser_CreatedBy",
table: "Users",
column: "IDUser_CreatedBy",
principalTable: "Users",
principalColumn: "IDUser",
onDelete: ReferentialAction.Restrict);
migrationBuilder.AddForeignKey(
name: "FK_Users_Users_UserIDUser",
table: "Users",
column: "UserIDUser",
principalTable: "Users",
principalColumn: "IDUser",
onDelete: ReferentialAction.Restrict);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Users_Users_IDUser_CreatedBy",
table: "Users");
migrationBuilder.DropForeignKey(
name: "FK_Users_Users_UserIDUser",
table: "Users");
migrationBuilder.DropIndex(
name: "IX_Users_IDUser_CreatedBy",
table: "Users");
migrationBuilder.DropColumn(
name: "IDUser_CreatedBy",
table: "Users");
migrationBuilder.RenameColumn(
name: "UserIDUser",
table: "Users",
newName: "CreatedByIDUser");
migrationBuilder.RenameIndex(
name: "IX_Users_UserIDUser",
table: "Users",
newName: "IX_Users_CreatedByIDUser");
migrationBuilder.AddForeignKey(
name: "FK_Users_Users_CreatedByIDUser",
table: "Users",
column: "CreatedByIDUser",
principalTable: "Users",
principalColumn: "IDUser",
onDelete: ReferentialAction.Restrict);
}
Produce una colonna ForeignKey che volevo ( IDUser_CreatedBy
) e un indice valido per quella colonna chiave ( IX_Users_IDUser_CreatedBy
) ma aggiunge anche un'altra ForeignKey ( UserIDUser
) che è una proprietà shadow, suppongo. Quindi come risultato ho 2 diverse colonne ForeignKey che si riferiscono a una colonna di IDUser
( IDUser
) nella stessa tabella. Nel mio caso, infatti, la migrazione rinomina la vecchia colonna ( CreatedByIDUser
) in una nuova ( UserIDUser
), che è un resto della mia precedente colonna shadow ShadowKey. Quello che sto cercando è che la migrazione eliminerà completamente la precedente proprietà shadow e introdurrà solo una, nuova colonna IDUser_CreatedBy
( IDUser_CreatedBy
). Eventuali suggerimenti ?
MODIFICA 2
@viveknuna: grazie per il tuo esempio ma anche per me non fa il lavoro.
La migrazione ha questo aspetto:
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Users_Users_CreatedByIDUser",
table: "Users");
migrationBuilder.RenameColumn(
name: "CreatedByIDUser",
table: "Users",
newName: "User_CreatedByIDUser");
migrationBuilder.RenameIndex(
name: "IX_Users_CreatedByIDUser",
table: "Users",
newName: "IX_Users_User_CreatedByIDUser");
migrationBuilder.AddColumn<int>(
name: "IDUser_CreatedBy",
table: "Users",
nullable: true);
migrationBuilder.AddForeignKey(
name: "FK_Users_Users_User_CreatedByIDUser",
table: "Users",
column: "User_CreatedByIDUser",
principalTable: "Users",
principalColumn: "IDUser",
onDelete: ReferentialAction.Restrict);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Users_Users_User_CreatedByIDUser",
table: "Users");
migrationBuilder.DropColumn(
name: "IDUser_CreatedBy",
table: "Users");
migrationBuilder.RenameColumn(
name: "User_CreatedByIDUser",
table: "Users",
newName: "CreatedByIDUser");
migrationBuilder.RenameIndex(
name: "IX_Users_User_CreatedByIDUser",
table: "Users",
newName: "IX_Users_CreatedByIDUser");
migrationBuilder.AddForeignKey(
name: "FK_Users_Users_CreatedByIDUser",
table: "Users",
column: "CreatedByIDUser",
principalTable: "Users",
principalColumn: "IDUser",
onDelete: ReferentialAction.Restrict);
}
Rinomina la mia proprietà shadow residua ForeignKey con un nuovo nome ma mantiene quella proprietà shadow. La migrazione aggiunge anche una nuova colonna IDUser_CreatedBy
che non è una ForeignKey (non esiste alcun metodo AddForeignKey
e CreateIndex
per quella colonna).
MODIFICA 3
@ Ivan Stoev - hai ragione. Non ho inviato l'intero modello perché pensavo che non avrebbe avuto alcun impatto sul risultato, ma ora è chiaro che ha ... :) Prima di tutto - ho davvero creato un nuovo progetto da riga di comando e sono riuscito a impalcare la corretta migrazione come tu hai detto. Poi ho capito che il mio modello ha un'altra proprietà public List<User> UsersAdded { get; set; }
che descrive tutti gli utenti aggiunti da quel particolare User
. E questo è il colpevole. Aggiunge la nuova colonna UserIDUser
alla migrazione appena creata. Quindi la domanda finale è: come posso ottenere tutto ciò di cui ho bisogno con quella nuova proprietà public List<User> UsersAdded { get; set; }
? perché voglio avere l'opportunità di includere la raccolta di User
che questo User
creato mentre ricevevo un User
nel mio controller MVC. Come formalità invio il mio modello completo (correttamente questa volta):
public class User
{
[Key]
public int IDUser { get; set; }
[Required]
public string Forename { get; set; }
[Required]
public string Name { get; set; }
public string AvatarPath { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public string Password { get; set; }
public bool IsWebUser { get; set; }
public DateTimeOffset CreatedAt { get; set; }
public int? IDUser_CreatedBy { get; set; }
public User User_CreatedBy { get; set; }
public List<User> UsersAdded { get; set; }
}
L'API fluente di EF Core offre un controllo completo sulla denominazione delle colonne della tabella, indipendentemente dal fatto che si utilizzi la proprietà del modello esplicito o shadow. La proprietà non convenzionale della chiave esterna viene mappata con l'API fluente di HasForeignKey
della configurazione della relazione .
Il tuo modello di entità cade in Proprietà di navigazione singola con categoria di proprietà FK esplicita, che può essere mappata in questo modo:
modelBuilder.Entity<User>()
.HasOne(e => e.User_CreatedBy) // reference navigation property
.WithMany() // no collection navigation property
.HasForeignKey(e => e.IDUser_CreatedBy); // foreign key property