在 Entity Framework Code First 中将多个 Table 与单个通用目的 Table 相关联
Relate Multiple Tables to Single General Purpose Table in Entity Framework Code First
很多时候我有一个其他实体包含的通用实体集合。我不想为每个需要它的父实体类型创建一个新的集合实体,但想重新使用一个通用实体。出于性能原因,我也不想像 this answer 那样显式定义多对多关系。最简单的示例是字符串集合。
public class MyString
{
public Guid Id { get; set; }
public string Value { get; set; }
}
public class MyEntity
{
public Guid Id { get; set; }
public virtual List<MyString> { get; set; }
}
public class MyOtherString
{
public Guid Id { get; set; }
public string Value { get; set; }
}
public class MyOtherEntity
{
public Guid Id { get; set; }
public virtual List<MyOtherString> { get; set; }
}
我真的很想将 MyString
和 MyOtherString
合并为一个实体:
public class GeneralPurposeString
{
public Guid Id { get; set; }
public string Value { get; set; }
}
public class MyEntity
{
public Guid Id { get; set; }
public virtual List<GeneralPurposeString> { get; set; }
}
public class MyOtherEntity
{
public Guid Id { get; set; }
public virtual List<GeneralPurposeString> { get; set; }
}
除了现在我要在 GeneralPurposeString
中为包含 GeneralPurposeString
.
集合的每个实体添加一个额外的外键
我想要的是在 GeneralPurposeString
table(但不是实体)上增加一个父类别列,以指定该项目属于哪个实体。我使用 Guid
作为主键,所以 table 看起来像这样:
CREATE TABLE [GeneralPurposeString]
(
[Id] uniqueidentifier NOT NULL
CONSTRAINT PK_GeneralPurposeString PRIMARY KEY,
[ParentEntityCategory] uniqueidentifier NOT NULL,
[ParentEntityId] uniqueidentifier NOT NULL,
[Value] nvarchar(MAX)
)
以及一些如何在 Code First 中指定 MyEntity
具有特定类别,并且 GeneralPurposeString
的集合使用该类别,而 MyOtherEntity
使用另一个类别(Guid)因为它是 GeneralPurposeString
.
的集合
关键是 GeneralPurposeString
可以是任何其他实体中的集合,并且加载父实体和包含集合将自动加载,而无需显式指定类别。
所有这些的目的是
- 允许 .NET 代码具有
GeneralPurposeString
未在任何地方复制的代码(实际实用程序或业务逻辑代码)。这可能也可以通过继承和显式映射来完成,但是这仍然会在数据库中留下多个 table(请参阅 #2)。
GeneralPurposeString
的数据库中只有一个 table。这更像是一个整洁问题。多个 table 的性能可能会更好,但是在 ParentEntityCategory
/ParentEntityId
上建立索引并覆盖 Value
应该是查找的良好性能。
- 不必在需要的地方显式编码此关系和查找。
我在想,如果我能克服 #2 并在幕后使用单独的 table 并实现派生的 class,那将是最简单的方法。
所以只是:
public class GeneralPurposeString
{
public Guid Id { get; set; }
public string Value { get; set; }
}
// It's just a GeneralPurposeString with a fancy MyEntity membership pin
public class MyEntityString: GeneralPurposeString {}
public class MyEntity
{
public Guid Id { get; set; }
public virtual List<MyEntityString> Strings { get; set; }
}
// Cool GeneralPurposeStrings belong to MyOtherEntity
public class MyOtherEntityString: GeneralPurposeString {}
public class MyOtherEntity
{
public Guid Id { get; set; }
public virtual List<MyOtherEntityString> Strings { get; set; }
}
public class MyContext: DbContext
{
public DbSet<MyEntity> MyEntities { get; set; }
public DbSet<MyOtherEntity> MyOtherEntities { get; set; }
}
我不必将派生的 classes 添加到 DbContext
并且 tables 默认使用派生的 class 的复数命名, 所以它实际上非常简单。
我之前对父类别的思路需要额外的 coding/annotation,即使 EF 支持它也是如此。这纯粹使用约定,在注释或 OnModelCreating()
.
中不需要任何额外的东西
我目前没有发现额外的 table 有任何危害。我认为(目前)不需要将所有数据集中在一个 table 中进行报告,但这实际上取决于通用实体的类型,所以我可能需要在未来重新审视它,或者如果我确实需要一个 table.
中的数据,我可能会选择多对多路线
我还能拥有:
public static class GeneralPurposeStringExtensions
{
public static void SassThatHoopyFrood(this GeneralPurposeString s)
{
// do stuff
}
}
很多时候我有一个其他实体包含的通用实体集合。我不想为每个需要它的父实体类型创建一个新的集合实体,但想重新使用一个通用实体。出于性能原因,我也不想像 this answer 那样显式定义多对多关系。最简单的示例是字符串集合。
public class MyString
{
public Guid Id { get; set; }
public string Value { get; set; }
}
public class MyEntity
{
public Guid Id { get; set; }
public virtual List<MyString> { get; set; }
}
public class MyOtherString
{
public Guid Id { get; set; }
public string Value { get; set; }
}
public class MyOtherEntity
{
public Guid Id { get; set; }
public virtual List<MyOtherString> { get; set; }
}
我真的很想将 MyString
和 MyOtherString
合并为一个实体:
public class GeneralPurposeString
{
public Guid Id { get; set; }
public string Value { get; set; }
}
public class MyEntity
{
public Guid Id { get; set; }
public virtual List<GeneralPurposeString> { get; set; }
}
public class MyOtherEntity
{
public Guid Id { get; set; }
public virtual List<GeneralPurposeString> { get; set; }
}
除了现在我要在 GeneralPurposeString
中为包含 GeneralPurposeString
.
我想要的是在 GeneralPurposeString
table(但不是实体)上增加一个父类别列,以指定该项目属于哪个实体。我使用 Guid
作为主键,所以 table 看起来像这样:
CREATE TABLE [GeneralPurposeString]
(
[Id] uniqueidentifier NOT NULL
CONSTRAINT PK_GeneralPurposeString PRIMARY KEY,
[ParentEntityCategory] uniqueidentifier NOT NULL,
[ParentEntityId] uniqueidentifier NOT NULL,
[Value] nvarchar(MAX)
)
以及一些如何在 Code First 中指定 MyEntity
具有特定类别,并且 GeneralPurposeString
的集合使用该类别,而 MyOtherEntity
使用另一个类别(Guid)因为它是 GeneralPurposeString
.
关键是 GeneralPurposeString
可以是任何其他实体中的集合,并且加载父实体和包含集合将自动加载,而无需显式指定类别。
所有这些的目的是
- 允许 .NET 代码具有
GeneralPurposeString
未在任何地方复制的代码(实际实用程序或业务逻辑代码)。这可能也可以通过继承和显式映射来完成,但是这仍然会在数据库中留下多个 table(请参阅 #2)。 GeneralPurposeString
的数据库中只有一个 table。这更像是一个整洁问题。多个 table 的性能可能会更好,但是在ParentEntityCategory
/ParentEntityId
上建立索引并覆盖Value
应该是查找的良好性能。- 不必在需要的地方显式编码此关系和查找。
我在想,如果我能克服 #2 并在幕后使用单独的 table 并实现派生的 class,那将是最简单的方法。
所以只是:
public class GeneralPurposeString
{
public Guid Id { get; set; }
public string Value { get; set; }
}
// It's just a GeneralPurposeString with a fancy MyEntity membership pin
public class MyEntityString: GeneralPurposeString {}
public class MyEntity
{
public Guid Id { get; set; }
public virtual List<MyEntityString> Strings { get; set; }
}
// Cool GeneralPurposeStrings belong to MyOtherEntity
public class MyOtherEntityString: GeneralPurposeString {}
public class MyOtherEntity
{
public Guid Id { get; set; }
public virtual List<MyOtherEntityString> Strings { get; set; }
}
public class MyContext: DbContext
{
public DbSet<MyEntity> MyEntities { get; set; }
public DbSet<MyOtherEntity> MyOtherEntities { get; set; }
}
我不必将派生的 classes 添加到 DbContext
并且 tables 默认使用派生的 class 的复数命名, 所以它实际上非常简单。
我之前对父类别的思路需要额外的 coding/annotation,即使 EF 支持它也是如此。这纯粹使用约定,在注释或 OnModelCreating()
.
我目前没有发现额外的 table 有任何危害。我认为(目前)不需要将所有数据集中在一个 table 中进行报告,但这实际上取决于通用实体的类型,所以我可能需要在未来重新审视它,或者如果我确实需要一个 table.
中的数据,我可能会选择多对多路线我还能拥有:
public static class GeneralPurposeStringExtensions
{
public static void SassThatHoopyFrood(this GeneralPurposeString s)
{
// do stuff
}
}