如何正确实现实体框架核心类以支持两个类之间的多对多关系,但是这些类和关系的数组/数据来自两个或更多数据源。
这就是说,如果我们有一个简单的关系,例如:
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class Tag
{
public string TagId { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class PostTag
{
public int PostId { get; set; }
public Post Post { get; set; }
public string TagId { get; set; }
public Tag Tag { get; set; }
}
我们的服务器将通过Web api从另外两个或数据源中获取一系列帖子,标签以及它们之间的任何关系。因此,如果我们只是尝试直接获取数据并将其序列化回我们的数据库,则在给定2个或更多不同源的情况下,Id密钥会发生冲突。存储相同类结构的差异源并且不丢失关系信息的正确方法是什么?谢谢
答案 0 :(得分:1)
您缺少转换层的内容。让我解释一下。
您存储在数据库中的post对象必须具有以下结构:
发布表
+------------+----------------+-------+----------+
| PostId | ExternalPostId | Title | Content |
+------------+----------------+-------+----------+
标签表
+--------+---------------+-------------+-------------+
| TagId | ExternalTagId | OtherField1 | OtherField2 |
+--------+---------------+-------------+-------------+
联接表
+------------+-------+
| PostId | TagId |
+------------+-------+
Post
表。Tag
表。此时,您的实体将如下所示:
邮政实体
[Table("Post")]
public class DbPost {
[Key]
[Column("PostId")]
public int Id { get; set; }
[Column("ExternalPostId")]
public int ExternalId { get; set; }
[Column("Title")]
public string Title { get; set; }
[Column("Content")]
public string Content { get; set; }
public IList<PostTag> PostTags { get; set; }
}
标签实体:
[Table("Tag")
public class DbTag
{
[Key]
[Column("TagId")]
public int Id { get; set; }
[Column("ExternalTagId")]
public string ExternalId { get; set; }
public IList<PostTag> PostTags { get; set; }
}
PostTag实体 该实体保持不变。此处的区别是PostId映射到Post实体的Id属性,这是您自己的Post表的主键。 TagId以类似的方式映射到Tag表的Id属性,这也是您自己的表的主键。
public class DbPostTag
{
public int PostId { get; set; }
public Post Post { get; set; }
public int TagId { get; set; }
public Tag Tag { get; set; }
}
最后,您的域上下文:
public class Database : DbContext {
public virtual DbSet<DbPost> Posts { get; set; }
public virtual DbSet<DbTag> Tags { get; set; }
public virtual DbSet<DbPostTag> PostTags { get; set; }
...
}
当您从API插入帖子时,您需要在从API到数据库中的帖子实体之间创建翻译。您可以通过扩展方法或为您翻译的方法来实现:
public static class ApiObjectExtensions {
public static DbPost ToDbPost(this Post post){
var dbPost = new DbPost(){
ExternalId = post.PostId,
Title = post.Title,
Content = post.Content,
PostTags = new List<DbPostTags>()
};
}
public static DbTag ToDbTag(this Tag tag){
return new DbTag(){
ExternalId = tag.Id,
PostTags = new List<DbPostTags>()
};
}
}
因此,当您在域上下文中插入数据时,只需转换相应的实体即可。该代码假定您确实具有上述对象图,并且您的帖子具有一堆post标签,并且每个post标签都有一堆标签。
foreach(var post in posts){
var dbPost = post.ToDbPost();
if(post.PostTags != null && post.PostTags.Any()){
foreach(var postTag in post.PostTags){
if(postTag.Tag != null){
var dbTag = postTag.Tag.ToDbTag();
var dbPostTag = new DbPostTag(){
Post = dbPost,
Tag = dbTag
};
dbPost.PostTags.Add(dbPostTag);
}
}
}
Database.DbPosts.Add(dbPost);
}
Database.SaveChanges();
如果您要接收帖子,帖子标签以及在单独调用中的标签,则与插入操作有所不同:
var insertedPosts = new List<DbPost>();
var insertedTags = new List<DbTag>();
foreach(var post in posts){
var dbPost = post.ToDbPost();
Database.Posts.Add(dbPost);
insertedPosts.Add(dbPost);
}
foreach(var tag in tags){
var dbTag = tag.ToDbTag();
Database.Tags.Add(dbTag);
insertedTags.Add(dbTag);
}
Database.SaveChanges();
这会将所有数据库发布和数据库标签实体写入数据库并分配其主键。 下一步是将两者链接在一起。
foreach(var postTag in postTags){
var dbPost = insertedPosts.FirstOrDefault(p => p.ExternalId = postTag.PostId);
var dbTag = insertedTags.FirstOrDefault(t => t.ExternalId = postTag.TagId);
if(dbPost != null && dbTag != null){
var dbPostTag = new DbPostTag(){
PostId = dbPost.Id,
Tag = dbTag.Id
};
Database.PostTags.Add(dbPostTag);
}
}
Database.SaveChanges();
最后一部分是当您取回数据并且想要原始格式的数据时。 以下查询将返回所有帖子,它们的链接实体及其标签:
var dbPosts = this.Database.Posts
.Include( p => p.PostTags )
.ThenInclude(pt => pt.Tag )
.ToList();
var apiPosts = dbPosts.Select(p => p.ToApiPost()).ToList();
要将内容转换回原始的Post,PostTag和Tag类型,您可以简单地创建扩展方法,如下所示:
public static class DbObjectExtensions {
public static Post ToApiPost(this DbPost dbPost){
var post = new Post(){
PostId = dbPost.ExternalId,
Title = dbPost.Title,
Content = dbPost.Content,
PostTags = new List<PostTag>()
};
if(dbPost.PostTags != null) {
foreach(var dbPostTag in dbPost.PostTags){
if(dbPostTag.Tag != null){
var tag = dbPostTag.Tag.ToApiTag();
var postTag = new PostTag(){
PostId = post.PostId,
Post = post,
TagId = tag.TagId,
Tag = tag
};
post.PostTags.Add(postTag);
tag.PostTags.Add(postTag);
}
}
}
return post;
}
public static Tag ToApiTag(this DbTag dbTag){
return new Tag(){
TagId = dbTag.ExternalId
};
}
}