Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/asp.net-core/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Asp.net core 实体框架核心存储来自多个数据源的多对多关系_Asp.net Core_.net Core_Entity Framework Core - Fatal编程技术网

Asp.net core 实体框架核心存储来自多个数据源的多对多关系

Asp.net core 实体框架核心存储来自多个数据源的多对多关系,asp.net-core,.net-core,entity-framework-core,Asp.net Core,.net Core,Entity Framework Core,如何正确实现实体框架核心类以支持两个类之间的多对多关系,但这些类和关系的数组/数据来自2个或更多数据源 也就是说,如果我们有一个简单的关系,比如: public class Post { public int PostId { get; set; } public string Title { get; set; } public string Content { get; set; } public List<PostTag> PostTags {

如何正确实现实体框架核心类以支持两个类之间的多对多关系,但这些类和关系的数组/数据来自2个或更多数据源

也就是说,如果我们有一个简单的关系,比如:

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; }
}
公共类职位
{
公共int PostId{get;set;}
公共字符串标题{get;set;}
公共字符串内容{get;set;}
公共列表PostTags{get;set;}
}
公共类标签
{
公共字符串TagId{get;set;}
公共列表PostTags{get;set;}
}
公共类PostTag
{
公共int PostId{get;set;}
公共Post Post{get;set;}
公共字符串TagId{get;set;}
公共标记{get;set;}
}

我们的服务器将通过一个web api从两个或多个数据源获取一系列帖子、标签以及它们之间的任何关系。因此,如果我们只是简单地尝试直接获取数据并将其序列化回数据库,那么在给定2个或更多不同源的情况下,会与Id键发生冲突。存储同一类结构的不同源以及不丢失关系信息的正确方法是什么?谢谢

您缺少的是转换层。让我解释一下

存储在数据库中的post对象必须具有以下结构:

Post表

  • PostId-这是您自己表中的主键。自动生成,必需
  • ExternalPostId—这是从API到达的PostId
  • 标题-这是从API到达的标题
  • 内容-这是从API到达的内容
标记表

  • TagId-这是您自己的标记表的主键。自动生成,必需
  • ExternalTagId-这是从API到达的TagId
  • OtherField1、OtherField2-任何其他字段都会与API中的标记一起到达
联接表

  • PostId,TagId-复合主键
  • PostId-外键,必填,非自动编号,参考
    Post
  • TagId-外键,必需,非自动编号,参考
    标记
此时,您的实体将如下所示:

Post Enity

最后,您的域上下文:

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; }
  ...
}
如果您在单独的调用中接收post、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(); 
最后一项是当您检索回数据时,您希望它以原始格式显示。 以下查询将返回所有帖子、它们的链接实体和它们的标记:

var dbPosts = this.Database.Posts
    .Include( p => p.PostTags )
    .ThenInclude(pt => pt.Tag )
    .ToList();

var apiPosts = dbPosts.Select(p => p.ToApiPost()).ToList();
要将内容转换回原始的Post、PostTag和标记类型,只需创建如下扩展方法:

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
    };
  }
}
公共静态类DbObjectExtensions{
公共静态Post到PIPOST(此DbPost){
var post=new post(){
PostId=dbPost.ExternalId,
Title=dbPost.Title,
Content=dbPost.Content,
PostTags=新列表()
};
if(dbPost.PostTags!=null){
foreach(dbPost.PostTags中的var dbPostTag){
if(dbPostTag.Tag!=null){
var tag=dbPostTag.tag.ToApiTag();
var postTag=new postTag(){
posted=post.posted,
Post=Post,
TagId=tag.TagId,
标签=标签
};
post.PostTags.Add(postTag);
tag.PostTags.Add(postTag);
}
}
}
回程站;
}
公共静态标记ToApiTag(此DbTag DbTag){
返回新标记(){
TagId=dbTag.ExternalId
};
}
}

这是一个相当广泛的问题。事实上,你在问:我如何处理并发?我认为
Post
应该包含
List
而不是
List
@viveknuna不,EF-core不支持那种多对多关系。@GertArnold我在想做一些类似的事情:public-class-repost:Post{[Key]public-int-Id{get;set;}这种方法可以添加一个额外的Id键作为实际的DB键,然后修复导入的数据和关系,但如果您有多对多关系,这似乎是不可能的。看看下面的答案。
[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; }
}
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; }
  ...
}
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();
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();
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
    };
  }
}