Ho un'entità per Users
e un'entità per Projects
.
Devo essere in grado di assegnare più utenti a 3 diverse proprietà dell'elenco sulla mia entità di progetto. Sono stato in grado di farlo con successo per una proprietà (la relazione molte a molte) da un'entità join. Potrei specificare UserType
nella tabella Users e usare solo una proprietà, ma potrei imbattermi in scenari in cui gli utenti possono svolgere più di un ruolo (tipo) e quindi non funzionerebbe.
Pensavo di poter semplicemente inserire UserType
nella tabella di join (entità), ma non ho idea di come costruire quell'entità nel mio DBContext.
Ecco cosa ho che funziona con una proprietà definita:
ProjectEntity:
public class Project : IInt32Identity
{
public int Id { get; set; }
public string ProjectName { get; set; }
...
public bool ProjectActive { get; set; }
public List<ProjectFile> ProjectFiles { get; set; }
public List<ProjectUsers> ProjectUsers { get; set; }
public DateTime ProjectCreatedDate { get; set; }
public DateTime ProjectModifiedDate { get; set; }
}
UserEntity:
public class User : IInt32Identity
{
public int Id { get; set; }
public string UserEmail { get; set; }
...
public List<ProjectUsers> ProjectUsers { get; set; }
public DateTime UserCreatedDate { get; set; }
public DateTime UserLastLoggedInDate { get; set; }
public DateTime UserModifiedDate { get; set; }
}
JoinEntity:
public class ProjectUsers
{
public int UserId { get; set; }
public User User { get; set; }
public int ProjectId { get; set; }
public Project Project { get; set; }
}
E il mio OnModelCreating()
nel mio DBContext
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ProjectUsers>()
.HasKey(bc => new { bc.UserId, bc.ProjectId });
modelBuilder.Entity<ProjectUsers>()
.HasOne(bc => bc.User)
.WithMany(b => b.ProjectUsers)
.HasForeignKey(bc => bc.UserId);
modelBuilder.Entity<ProjectUsers>()
.HasOne(bc => bc.Project)
.WithMany(c => c.ProjectUsers)
.HasForeignKey(bc => bc.ProjectId);
}
Tutto funziona bene come ho detto sopra, ma ecco cosa vorrei:
ProjectEntity:
public class Project : IInt32Identity
{
public int Id { get; set; }
public string ProjectName { get; set; }
...
public bool ProjectActive { get; set; }
public List<ProjectFile> ProjectFiles { get; set; }
public List<ProjectUsers> ProjectClients { get; set; }
public List<ProjectUsers> ProjectBuilders { get; set; }
public List<ProjectUsers> ProjectDesigners { get; set; }
public DateTime ProjectCreatedDate { get; set; }
public DateTime ProjectModifiedDate { get; set; }
}
UserEntity è lo stesso.
JoinEntity:
public class ProjectUsers
{
public int UserId { get; set; }
public User User { get; set; }
public int ProjectId { get; set; }
public Project Project { get; set; }
public string UserType { get; set; }
}
Dove mi sono perso è sul codice OnModelBinding()
e non sono nemmeno sicuro che EF sarebbe abbastanza intelligente da popolare correttamente gli elenchi in base a quella meta proprietà UserType
.
Qualsiasi aiuto o guida sarebbe molto apprezzato.
TIA
Potrebbe sembrare possibile trattare ProjectUser
come classe / entità di base e creare classi / entità / tipi diversi per ProjectClient
, ProjectBuilder
e ProjectDesigner
ereditati da ProjectUser
. E poi crei tabelle per ogni tipo e relazione uno-a-molti con il progetto. Questo è in genere chiamato approccio TPT (Table Per Type) .
Tuttavia, TPT non è ancora implementato in EF Core.
Puoi ancora raggiungerlo utilizzando Table Per Hierarchy (TPH) , ma avrai un solo elenco nel progetto per tutti gli utenti del progetto, in cui UserId
, ProjectId
e UserType
diventano la chiave complessa. I clienti del progetto, i costruttori e i progettisti saranno calcolati dalle proprietà di quell'elenco utenti di un progetto.
public class Project
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ProjectUser> ProjectUsers { get; set; }
public IEnumerable<ProjectUser> ProjectClients => this.ProjectUsers
.Where(x => x.UserType == "Client");
public IEnumerable<ProjectUser> ProjectBuilders => this.ProjectUsers
.Where(x => x.UserType == "Builder");
public IEnumerable<ProjectUser> ProjectDesigners => this.ProjectUsers
.Where(x => x.UserType == "Designer");
}
public class User
{
public int Id { get; set; }
public string Email { get; set; }
public virtual ICollection<ProjectUser> UserProjects { get; set; }
}
public class ProjectUser
{
public int UserId { get; set; }
public virtual User User { get; set; }
public int ProjectId { get; set; }
public virtual Project Project { get; set; }
public string UserType { get; set; }
}
public class ProjectConfiguration : IEntityTypeConfiguration<Project>
{
public void Configure(EntityTypeBuilder<Project> builder)
{
builder.HasKey(x => x.Id);
builder.Property(x => x.Name).IsRequired();
builder.HasIndex(x => x.Name).IsUnique();
builder.Ignore(x => x.ProjectBuilders);
builder.Ignore(x => x.ProjectClients);
builder.Ignore(x => x.ProjectDesigners);
builder.ToTable("Project");
}
}
public class UserConfiguration : IEntityTypeConfiguration<User>
{
public void Configure(EntityTypeBuilder<User> builder)
{
builder.HasKey(x => x.Id);
builder.Property(x => x.Email).IsRequired();
builder.HasIndex(x => x.Email).IsUnique();
builder.ToTable("User");
}
}
public class ProjectUserConfiguration : IEntityTypeConfiguration<ProjectUser>
{
public void Configure(EntityTypeBuilder<ProjectUser> builder)
{
builder.HasKey(x => new { x.ProjectId, x.UserId, x.UserType });
builder.Property(x => x.UserType).IsRequired();
builder.HasOne(x => x.Project)
.WithMany(x => x.ProjectUsers)
.HasForeignKey(x => x.ProjectId);
builder.HasOne(x => x.User)
.WithMany(x => x.UserProjects)
.HasForeignKey(x => x.UserId);
}
}
La parola chiave virtual
è lì per il supporto del caricamento lento. Se non stai eseguendo un caricamento lento, non devi avere virtual
lì. Inoltre devi [NotMapped]
quelle 3 proprietà calcolate, che è lo stesso che usare. .Ignore
nel parlare delle API fluenti.
public class AppDbContext : DbContext
{
public DbSet<Project> Projects { get; set; }
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfiguration(new ProjectConfiguration());
modelBuilder.ApplyConfiguration(new UserConfiguration());
modelBuilder.ApplyConfiguration(new ProjectUserConfiguration());
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder
.UseLazyLoadingProxies()
.UseSqlServer("Data Source=.\\SQLEXPRESS;Initial Catalog=DL.SO.ProjectUsersDemo;Integrated Security=True;MultipleActiveResultSets=False;");
}
}
Niente di speciale qui. Dopo aver aggiunto la migrazione e aggiornato il database, il tuo dovrebbe apparire come
Dopo aver eseguito il seeding del database con dati di esempio, sebbene sia difficile mostrarli qui, puoi vedere che 3 elenchi sono pieni di dati corretti: