我正在使用Code First方法在Entity Framework中构建数据模型,但有一部分让我有点难过。这个问题的标题可能有点令人困惑,所以我会详细解释我的问题。这篇文章的篇幅可能令人生畏,但我认为这是一个相当简单的问题。
我有一个这样定义的模型:
public class KeyValuePair
{
[Key]
[MaxLength(128)]
[Column(Order = 0)]
public virtual string OwnerId { get; set; }
[Key]
[MaxLength(128)]
[Column(Order = 1)]
public virtual string Key { get; set; }
public virtual string Value { get; set; }
}
我的目的是为了只定义一个通用表,用于在系统中的其他实体上存储键值属性。我正在为所有ID使用GUID,因此OwnerId
应唯一地引用系统中的一个实体,而(OwnerId, Key)
对应唯一地标识一个实体上的一个属性。
换句话说,我想允许系统中的多个表与此KeyValuePair
表具有一对多关系。
例如,如果我想存储ID为b4fc3e9a-2081-4989-b016-08ddd9f73db0
的Person的高度,我会在此表中存储一行:
OwnerId = "b4fc3e9a-2081-4989-b016-08ddd9f73db0"
Key = "Height"
Value = "70 in."
所以现在我想要从父实体到这个表定义导航属性,比如(以获取Person示例):
public class Person
{
[Key]
public virtual string Id { get; set; }
public virtual string Name { get; set; }
// I want this to be a navigation property
public ICollection<KeyValuePair> Properties { get; set; }
}
但我不确定如何定义Person
和KeyValuePair
之间的关系,以便实体框架知道它应该通过匹配{{来查找Person的属性。 1}} Person
针对Id
s&#39; KeyValuePair
。我无法在OwnerId
模型中定义外键,因为KeyValuePair
将在几个不同的表中引用ID。
看起来就像我可以执行以下操作来定义OwnerId
中Person
到KeyValuePair
之间的关系:
OnModelCreating
或者我甚至可以给KeyValuePairs提供他们自己的唯一ID,摆脱modelBuilder.Entity<Person>()
.HasMany(p => p.Properties).WithMany().Map(mp =>
{
mp.MapLeftKey("Id");
mp.MapRightKey("OwnerId", "Key");
mp.ToTable("PersonDetail");
});
,然后执行此操作:
OwnerId
但是这两种方法都涉及创建一个中间表来链接modelBuilder.Entity<Person>()
.HasMany(p => p.Properties).WithMany().Map(mp =>
{
mp.MapLeftKey("Id");
mp.MapRightKey("Id");
mp.ToTable("PersonDetail");
});
和Person
表,这似乎是在膨胀我的数据库模式和需要更昂贵的JOIN方面的过度开销查询数据。
那么有没有办法定义这种关系,以至于我不需要涉及中间表?我是否以错误的方式进行数据库设计?
旁注:对于任何想知道为什么我使用这种方法在我的实体上定义属性而不是简单地将固定属性添加到数据模型的人,我 am 使用固定数据模型中的属性(如果适用),但我正在构建的应用程序需要能够在运行时定义自定义属性。我还认为这个问题适用于多个表与共享表具有一维关系的其他可能情况。
答案 0 :(得分:0)
我能想到的唯一方法(而且我承认,这不是最好的想法,但它会做你所要求的)会有任何需要的课程与KeyValuePair
建立这种关系,实现一个包含完全实现的导航属性的抽象类,以及ID字段。通过&#34;完全实施&#34;我不是指一个实际的,映射的关系;我的意思是它应该使用DbContext
转到KeyValuePair
表,并实际获取ID给出的相关属性。
这样的事情:
public abstract class HasKeyValuePairs
{
[Key]
public virtual string Id { get; set; }
[NotMapped]
public ICollection<KeyValuePair> Properties
{
get
{
using(var db = new DbContext())
{
return db.KeyValuePairs.Where(kvp => kvp.OwnerID == this.ID);
}
}
}
}
假设您正在使用延迟加载(假设您正在使用virtual
关键字),那么这样做就不会有额外的开销,因为EF会有无论如何要回到数据库拿起属性,如果你曾经要求它们。您可能需要返回List
只是为了避免代码中的任何潜在ContextDisposedException
,但这至少会让您启动并运行。