EF Core 一对多对多对一关系
EF Core one to many to many to one relationship
我已经使用数据库很长时间了,但对 Entity Framework 还是个新手。我处理编程和数据库开发这两个方面。作为数据库开发人员,我尽量保持它的干净,所以我想出的这个结构对我来说效果很好,但我不确定 Entity Framework 是否支持它,因为我已经尝试了几天,使用不同的场景, Data Annotations 以及 Fluent API 但无法让它工作。
我想做的可能有点不合常规,但我想避免的是必须为每个区域复制一个文件 table 因此我定义了 1 个文件 table可以使用关系由多个区域使用。因此,我所拥有的是:一个 [公司、员工或项目] 可以有多个文件(一对多)。同样,文件 table 可以由任何区域获取(多对多,在这种情况下,它不是数据而是结构,希望这是有意义的)。文件记录仅与1个区域[公司、员工或项目]相关(多对一)。
此方法的明显优点是我可以避免管理 3 个文件 table,但它并没有就此结束。正如您从 FileAccess table 中看到的那样,而不是在这里有多个 table 或多个字段来表示指向多个 table 的指针,我只需要管理 1 table 用于文件访问。关键在 RelationTable 和 RelationId 而不是特定的 File.Id.
下面是我要完成的结构的简化示例。可以在 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
}
正如 Ivan 指出的那样,由于外键限制,EF 不支持这一点,但我能够想出一个可行的解决方案。但是,我必须警告您,我才刚刚进入 EF 的第 3 周,所以我不知道这可能会导致什么后果,但对于那些可能感兴趣的人,这就是我所做的。
事实证明(通过反复试验),EF 只需要 OnModelCreating 来连接对象之间的关系,它实际上并不需要创建 FK,因此我这样定义了关系:
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 });
当您添加上述代码和 运行 添加迁移时,它会导致添加以下代码,这将破坏更新数据库命令,因此您需要将其注释掉在向上函数中。
//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);
您需要对“向下”功能执行相同操作,否则您将无法回滚更改。
//migrationBuilder.DropForeignKey(
// name: "FK_Files_Projects_RelationTable_RelationId",
// table: "Files");
//migrationBuilder.DropForeignKey(
// name: "FK_Files_FileAccess_RelationTable_RelationId",
// table: "Files");
现在您可以执行更新数据库,它应该 运行 就好了。 运行 该应用程序也运行良好。我能够使用 EF 方法获取带有关联文件的项目,并使用 FileAccess 对象。但是,请记住,这是一种 hack,未来版本的 EF 可能不支持它。干杯!
我已经使用数据库很长时间了,但对 Entity Framework 还是个新手。我处理编程和数据库开发这两个方面。作为数据库开发人员,我尽量保持它的干净,所以我想出的这个结构对我来说效果很好,但我不确定 Entity Framework 是否支持它,因为我已经尝试了几天,使用不同的场景, Data Annotations 以及 Fluent API 但无法让它工作。
我想做的可能有点不合常规,但我想避免的是必须为每个区域复制一个文件 table 因此我定义了 1 个文件 table可以使用关系由多个区域使用。因此,我所拥有的是:一个 [公司、员工或项目] 可以有多个文件(一对多)。同样,文件 table 可以由任何区域获取(多对多,在这种情况下,它不是数据而是结构,希望这是有意义的)。文件记录仅与1个区域[公司、员工或项目]相关(多对一)。
此方法的明显优点是我可以避免管理 3 个文件 table,但它并没有就此结束。正如您从 FileAccess table 中看到的那样,而不是在这里有多个 table 或多个字段来表示指向多个 table 的指针,我只需要管理 1 table 用于文件访问。关键在 RelationTable 和 RelationId 而不是特定的 File.Id.
下面是我要完成的结构的简化示例。可以在 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
}
正如 Ivan 指出的那样,由于外键限制,EF 不支持这一点,但我能够想出一个可行的解决方案。但是,我必须警告您,我才刚刚进入 EF 的第 3 周,所以我不知道这可能会导致什么后果,但对于那些可能感兴趣的人,这就是我所做的。
事实证明(通过反复试验),EF 只需要 OnModelCreating 来连接对象之间的关系,它实际上并不需要创建 FK,因此我这样定义了关系:
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 });
当您添加上述代码和 运行 添加迁移时,它会导致添加以下代码,这将破坏更新数据库命令,因此您需要将其注释掉在向上函数中。
//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);
您需要对“向下”功能执行相同操作,否则您将无法回滚更改。
//migrationBuilder.DropForeignKey(
// name: "FK_Files_Projects_RelationTable_RelationId",
// table: "Files");
//migrationBuilder.DropForeignKey(
// name: "FK_Files_FileAccess_RelationTable_RelationId",
// table: "Files");
现在您可以执行更新数据库,它应该 运行 就好了。 运行 该应用程序也运行良好。我能够使用 EF 方法获取带有关联文件的项目,并使用 FileAccess 对象。但是,请记住,这是一种 hack,未来版本的 EF 可能不支持它。干杯!