实体框架将集合属性映射到一列
Posted
技术标签:
【中文标题】实体框架将集合属性映射到一列【英文标题】:Entity Framework map collection property to one column 【发布时间】:2017-09-20 23:08:12 【问题描述】:我有模特:
public class FlyerPage
public Guid Id get; set;
public string Picture get; set;
public int? Page get; set;
public string Text get; set;
public ICollection<string> Keywords get; set;
public Guid FlyerId get; set;
public virtual Flyer Flyer get; set;
我想将Keywords
映射到FlyerPage
表中的一列。
我认为将其拆分为用逗号分隔的值的最佳方法,例如
"one", "two", "three".
怎么做? 通过 FluentAPI。
编辑 - 到另一个表
The type 'ICollection<string>' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method
modelBuilder.Entity<FlyerPage>().Map(m =>
m.Property(p => p.Keywords);
m.ToTable("Keyword", "Flyers.Page");
);
编辑 2:好的,这项工作
modelBuilder.Entity<FlyerPage>().Map(m =>
m.Properties(p => p.Keywords);
m.ToTable("FlyerPageKeywords");
);
但是多对多如何连接呢?
【问题讨论】:
你为什么会选择这样做? 为什么?因为关键字在 Page 内,我不想创建另一个表来获取它。 你真的应该有一张不同的桌子。如果您懒得创建另一个表,那么编程可能不适合您! 【参考方案1】:根据您的目的(例如,如果您想按关键字搜索),您可以创建两个新表,一个用于Keyword
s(其中每条记录都是一个关键字),另一个用于映射@之间的关系987654324@ 和 Keyword
,包含 FlyerPage
和 Keyword
的 Id 作为外键。
通过关键字搜索可能会更有效(即使使用所有 JOIN),这样可以避免存储重复值,并且很可能更接近应用程序的逻辑。
你最终会得到两张表(还有一张会自动生成):
public class FlyerPage
public Guid Id get; set;
public string Picture get; set;
public int? Page get; set;
public string Text get; set;
public ICollection<Keyword> Keywords get; set;
public Guid FlyerId get; set;
public virtual Flyer Flyer get; set;
public class Keyword
public Guid Id get; set;
public string Value get; set;
public virtual ICollection<FlyerPage> FlyerPages get; set;
然后使用 FluentAPI 进行多对多映射:
modelBuilder.Entity<FlyerPage>()
.HasMany(t => t.Keywords)
.WithMany(t => t.FlyerPages)
.Map(m =>
m.ToTable("FlyerPageKeywords");
m.MapLeftKey("FlyerPageID");
m.MapRightKey("KeywordID");
);
我认为它会解决您的问题。我在 FluentAPI 中并不完全“流利”,您可能必须将类的构造函数中的集合初始化为空集合。
此外,根据documentation,建立双向关系(即在Keyword
对象中存储ICollection<FlyerPage>
)的原因是:
按照惯例,Code First 始终将单向关系解释为一对多。
【讨论】:
OP 已经说过他们不想这样做。 (但我同意这是正确的做法) 我不想按关键字搜索。事实上,大多数传单页面中的关键字都是相同的,所以......我不会重复它。多对多? 如果大多数传单页面的关键字都相同,我特别推荐使用这种方法(避免重复)。您通常以这种方式建模多对多关系。 如何做好?我的意思是,如果我使用数据库中的关键字添加新的 FlyerPages,而其中一些不是。然后它们不会被复制,只有那些新的会被添加到“关键字”表中。 我用您需要的 FluentAPI 代码更新了我的答案。要将新的Keyword
添加到FlyerPage
,您必须首先检查具有给定值的Keyword
是否存在。如果有,只需将其添加到您的FlyerPage
,如果没有,请创建一个新的Keyword
,然后添加它。您可以在 Keyword.Value
上定义一个 UNIQUE 约束,以确保避免重复值,尽管在 FluentAPI 中它不是很简单(参见 ***.com/a/23155759/4353712)【参考方案2】:
如果我正确理解了您的要求,您想要做一个糟糕的数据库实践来将逗号分隔的集合添加到记录的一列。 我不明白你试图以指定的方式完成什么。 无论如何实现您的要求。
//这确实是一个不好的做法。我强烈建议使用 Sándor Mátyás Márton 的解决方案,这是完成您的实际用例的最佳实践。
您需要使用不同的实体
public class FlyerPage
public Guid Id get; set;
public string Picture get; set;
public int? Page get; set;
public string Text get; set;
public string KeywordsCollection get; set;
public Guid FlyerId get; set;
public virtual Flyer Flyer get; set;
这里可以设置
KeywordsCollection = string.Join(",", flierPage.Keywords);
//flierPage is the viewmodel object and flierPage.Keywords is the collection
//you got from the viewmodel
【讨论】:
这是一种不好的做法? 是的。我在回答中已经说过了。寻求这个人的 Sándor Mátyás Márton 解决方案。尝试研究 DB Normalization 实践。但是请投票给我。如果你很确定这是你需要的,你可以接受这个作为答案【参考方案3】:如果您确实必须将标准化数据存储在单个字段中,那么您可以通过如下修改您的类来实现:
public class FlyerPage
public ICollection<string> Keywords
get
return _keywords.Split(',');
set
_keywords = string.Join(",", value);
private string _keywords;
然后在您的流利映射中,将Keywords
属性设置为忽略并映射_keywords
字段。
【讨论】:
现在你有一个问题关键字包含逗号。 确实如此,尽管通常关键字是没有标点符号的单个单词。 OP的例子就是这样。真正的问题是ICollection
应该是不可变的。
实际上还有一个客户端尝试调用page.Keywords.Add("newkeyword")
的问题,它实际上什么都不做。哦,你不能映射一个私有字段,所以你需要一个支持属性。【参考方案4】:
创建一个连接的关键字列表。
public class FlyerPage
.....
public string JoinedKeywords
get
return String.Join(",", this.Keywords);
public ICollection<string> Keywords get; set;
【讨论】:
现在你有一个问题关键字包含逗号。以上是关于实体框架将集合属性映射到一列的主要内容,如果未能解决你的问题,请参考以下文章