Ho lavorato con i database per molto tempo, ma sono nuovo in Entity Framework. Gestisco entrambi gli aspetti della programmazione e dello sviluppo del database. Come sviluppatore db, cerco di tenerlo pulito, quindi questa struttura che ho trovato funziona bene per me, ma non sono sicuro che Entity Framework lo supporti anche perché ho provato per diversi giorni, usando diversi scenari, Annotazioni dei dati così come l'API Fluent, ma non riusciva a farlo funzionare.
Quello che sto cercando di fare potrebbe essere un po 'non convenzionale, ma quello che sto cercando di evitare è dover duplicare una tabella file per ogni area, quindi definisco una tabella di file che può essere utilizzata da più aree utilizzando una relazione. Quindi, quello che ho è: una [azienda, dipendente o progetto] può avere molti file (uno a molti). Allo stesso modo, la tabella dei file può essere originata da qualsiasi area (da molti a molti, in questo caso, non sono i dati ma piuttosto la struttura, si spera che abbia senso). I record di file sono correlati solo a 1 area [azienda, dipendente o progetto] (molti a uno).
L'ovvio vantaggio di questo metodo è che posso evitare di dover gestire 3 tabelle di file ma non finisce qui. Come puoi vedere dalla tabella FileAccess, invece di avere più tabelle qui o più campi per rappresentare i puntatori alle tabelle multiple, ho solo bisogno di gestire 1 tabella per l'accesso ai file. La chiave è in RelationTable e RelationId anziché nello specifico File.Id.
Di seguito è riportato un esempio semplificato della struttura che sto cercando di realizzare. Può essere fatto in Entity Framework?
public class Company
{
public Guid Id { get; set; }
public string Name { get; set; }
public virtual ICollection<File> Files { get; set; }
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<File> Files { get; set; }
}
public class Project
{
public int Id { get; set; }
public Guid? CompanyId { get; set; }
public string ProjectNo {get; set; }
public virtual ICollection<File> Files { get; set; }
}
public class File
{
public int Id { get; set; }
public Int16 RelationTable { get; set; } 0=Company, 1=Employee, 2=Project
public string RelationId { get; set; } Company.Id, Employee.Id, Project.Id
public string FileName { get; set; }
}
public class FileAccess
{
public int Id { get; set; }
public int EmployeeId { get; set; }
public Int16 RelationTable { get; set; } 0=Company, 1=Employee, 2=Project
public string RelationId { get; set; } Company.Id, Employee.Id, Project.Id
public string AccessType
}
Come ha sottolineato Ivan, EF non lo supporta a causa dei limiti delle chiavi esterne, ma sono riuscito a trovare una soluzione funzionante. Tuttavia, devo avvertirti che sono solo sulla mia terza settimana di EF, quindi non so quali conseguenze ciò possa causare, ma questo è quello che ho fatto, per coloro che potrebbero essere interessati.
Come risulta (attraverso tentativi ed errori), EF ha solo bisogno di OnModelCreating per collegare la relazione tra gli oggetti, non ha davvero bisogno di creare l'FK, quindi ho definito la relazione in questo modo:
modelBuilder.Entity<File>()
.HasIndex(k => new { k.RelationTable, k.RelationId }); //for performance
modelBuilder.Entity<FileAccess>()
.HasMany(fa => fa.Files)
.WithOne(f => f.FileAccess)
.HasForeignKey(k => new { k.RelationTable, k.RelationId })
.HasPrincipalKey(k => new { k.RelationTable, k.RelationId });
//Using enumerations to control
relationships and adding PERSISTED so it doesn't need to be maintained plus it
won't break the Add-Migration with the "non persistent error"
modelBuilder.Entity<Project>()
.Property(f => f.RelationTable)
.HasComputedColumnSql((int)NTGE.Database.Shared.eFileRelTable.Projects + " PERSISTED") //This injects the value so we don't have to store it
.HasDefaultValue(123); //This doesn't really matter, we just need it so EF doesn't try to insert a value when saving, which will cause an error
modelBuilder.Entity<Project>()
.HasMany(p => p.Files)
.WithOne(f => f.Project)
.HasForeignKey(k => new { k.RelationTable, k.RelationId })
.HasPrincipalKey(k => new { k.RelationTable, k.Id });
Aggiungendo i codici sopra riportati ed eseguendo Add-Migration, si otterrà l'aggiunta dei seguenti codici, che interromperanno il comando Update-Database, quindi sarà necessario commentarli nella funzione Su.
//migrationBuilder.AddForeignKey(
// name: "FK_Files_Projects_RelationTable_RelationId",
// table: "Files",
// columns: new[] { "RelationTable", "RelationId" },
// principalTable: "Projects",
// principalColumns: new[] { "RelationTable", "Id" },
// onDelete: ReferentialAction.Cascade);
//migrationBuilder.AddForeignKey(
// name: "FK_Files_FileAccess_RelationTable_RelationId",
// table: "Files",
// columns: new[] { "RelationTable", "RelationId" },
// principalTable: "FileAccess",
// principalColumns: new[] { "RelationTable", "RelationId" },
// onDelete: ReferentialAction.Cascade);
Dovrai fare lo stesso con la funzione Giù altrimenti non sarai in grado di ripristinare le tue modifiche.
//migrationBuilder.DropForeignKey(
// name: "FK_Files_Projects_RelationTable_RelationId",
// table: "Files");
//migrationBuilder.DropForeignKey(
// name: "FK_Files_FileAccess_RelationTable_RelationId",
// table: "Files");
Ora puoi fare un Update-Database e dovrebbe funzionare bene. Anche l'esecuzione dell'app funziona perfettamente bene. Sono in grado di utilizzare il metodo EF per ottenere il progetto con i file associati e ho lavorato anche con l'oggetto FileAccess. Tuttavia, tieni presente che questo è un trucco e le versioni future di EF potrebbero non supportarlo. Saluti!