Что вам не хватает конверсионного слоя.Позвольте мне объяснить.
Ваш объект записи, хранящийся в базе данных, должен иметь следующую структуру:
Таблица сообщений
+------------+----------------+-------+----------+
| PostId | ExternalPostId | Title | Content |
+------------+----------------+-------+----------+
- PostId - это первичный ключ в вашей собственной таблице.Сгенерировано автоматически, обязательно.
- ExternalPostId - это PostId, получаемый из API.
- Title - это заголовок, получаемый из API
- Content- это контент, полученный от API.
Таблица тегов
+--------+---------------+-------------+-------------+
| TagId | ExternalTagId | OtherField1 | OtherField2 |
+--------+---------------+-------------+-------------+
- TagId - Thisявляется первичным ключом вашей собственной таблицы тегов.Требуется автоматически.
- ExternalTagId - это TagId в том виде, в каком он поступает из API
- OtherField1, OtherField2 - все остальные поля, полученные с тегом из API.
Присоединиться к таблице
+------------+-------+
| PostId | TagId |
+------------+-------+
- PostId, TagId - составной первичный ключ
- PostId - внешний ключ, обязательный, неавтоматическая нумерация ссылок на таблицу
Post
. - TagId - внешний ключ, обязательный, без нумерации ссылок на таблицу
Tag
.
В этот момент ваши объекты будутвыглядеть следующим образом:
Post Enity
[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; }
}
Tag entity:
[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 сопоставляется со свойством Id сущности Post, которое является вашим собственным первичным ключом вашей собственной таблицы Post.Аналогичным образом TagId сопоставляется со свойством Id таблицы Tag, которая также является вашим собственным первичным ключом вашей собственной таблицы.
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>()
};
}
}
Поэтому, когда вы вставляете данные в контекст домена, вам просто нужно преобразовать соответствующие объекты.В этом коде предполагается, что у вас действительно есть граф объектов, описанный выше, и в вашем сообщении есть куча тегов постов, а в каждом теге поста есть несколько тегов.
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();
Если вы получаете сообщения,теги post и теги в отдельных вызовах, отличные от вашей операции вставки, будут немного отличаться:
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();
Это запишет все сущности db post и db tag в db и назначит их первичные ключи.Следующим шагом будет связать их вместе.
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
};
}
}