当前位置: 首页 > 知识库问答 >
问题:

在EF Core 5中,如何通过只设置foreigns keys id而不首先查询来插入具有多对多关系的实体?

朱宇航
2023-03-14

另一个表包含ID众所周知的引用数据。用例是从文件中读取数据,创建实体,然后批量插入它们。我不需要首先查询任何内容,所以所有实体都与上下文“断开”连接。

简单示例:

public class Post
{
    public int ID { get; set; }
    public string Text { get; set; }
    public virtual ICollection<Tag> Tags { get; set; } 
}

public class Tag
{
    public int ID { get; set; }
    [Required]
    public string Label { get; set;}
    public virtual ICollection<Post> Posts { get; set; } 
}

第一次尝试

List<Post> posts = new List<Post>();

loop
  var post = new Post { Text = "some text"});
  post.Tags.Add(new Tag {ID = 1});  
  post.Tags.Add(new Tag {ID = 2});
  posts.Add(post);
...

context.Posts.AddRange(posts);
context.SaveChange();

错误,因为EF试图通过将其他标记列设置为null来更新标记记录。无论如何,我不希望EF更新标记表,只更新联接表。

第二次尝试
阅读长话短说:使用外键,它会拯救你的一天,我没有找到一种方法让它与FK集合一起工作,因为在我的情况下,它是多对多的关系。

第三次尝试,而不是使用上下文。邮递AddRange(帖子) ,我仅附加父实体:

var post = new Post { Text = "some text"});
post.Tags.Add(new Tag {ID = 1});
post.Tags.Add(new Tag {ID = 2});
context.Posts.Attach(post).State = EntityState.Added;
context.SaveChanges();

这起作用了。插入Post,联接表PostsTags包含关系数据,而Tags表保持不变。但这在批处理(相同的上下文)中是行不通的,因为我无法用相同的标记创建另一篇文章。上下文根据标签的ID“跟踪”标签,因此我无法插入具有相同ID的“新”标签。

第四次尝试,我现在做的不是添加一个新标签,而是添加一个新标签。标签。Add(新标记{ID=1}) ,我从db的帖子中添加了标签。标签。Add(context.Tags.Find(1)) 这意味着要多次访问数据库,以获取已知的信息。

我想到的其他选择是保留已附加到上下文的标记的本地词汇表,在每个帖子之间更改上下文,找到一种方法将数据直接插入表示联接表的实体类型,预先查询所有引用(但某些引用表包含数千个元素),或者只使用原始sql查询。

我无法想象没有简单的方法可以插入带有Fk id的模型,就像它通过使用外键属性为一对多工作一样。

非常感谢。

共有2个答案

公西承
2023-03-14

当您在Post和Tag之间有多对多关系时,EFCore会自动添加一个表来存储该关系。您需要使用POCO手动添加该表,然后使用该表添加关系。如图所示

以下是相关代码:

创建包含关系的类。

public class PostTag
{

    public int PostId { get; set; }
    public Post Post { get; set; }

    public int TagId { get; set; }
    public Tag Tag { get; set; }
}

然后在OnModelCreate方法中,添加如下关系:

protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<PostTag>()
            .HasKey(t => new { t.PostId, t.TagId });

        modelBuilder.Entity<PostTag>()
            .HasOne(pt => pt.Post)
            .WithMany(p => p.PostTags)
            .HasForeignKey(pt => pt.PostId);

        modelBuilder.Entity<PostTag>()
            .HasOne(pt => pt.Tag)
            .WithMany(t => t.PostTags)
            .HasForeignKey(pt => pt.TagId);
    }

或者,您可以使用注释来使用属性设置关系

然后您可以手动添加带有所需数据的PostTag实体。

孙玮
2023-03-14

问题将是由于跟踪或缺乏对标签的跟踪。由于您不想查询数据库,因此您可以选择附加标签实例,您可以保证这些实例是合法的标签行。如果您有合理的标签ID号可供使用,您可以创建并附加完整的集合以供参考。否则,您可以从传入的数据ID中派生它。

一、 e.如果我们有20个标签可供选择,ID 1-20:

for (int tagId = 1; tagId <= 20; tagId++)
{
    var tag = new Tag { Id = tagId };
    context.Tags.Attach(tag);
}

我们不需要在列表中单独跟踪这些标签。一旦与DbContext关联,我们就可以使用上下文。标签,或者要特别小心阅读,上下文。标签。本地然后在填充帖子时:

var post = new Post { Text = "some text"});
post.Tags.Add(context.Tags.Local.Single(x => x.Id == 1));  
post.Tags.Add(context.Tags.Local.Single(x => x.Id == 2));  
posts.Add(post);
//...

context.Posts.AddRange(posts);

如果您有大量标签并为指定要与每个新帖子关联的标签ID的帖子传递一个结构,那么您可以从中构建一个列表:

var tags = postViewModels.SelectMany(x => x.TagIds)
    .Distinct()
    .Select(t => new Tag { Id == t)).ToList();

例如,提供的一组后视图模型包含一个标记ID列表。我们选择所有不同的标记ID,然后构建要关联的标记。

这里需要注意的是,DbContext是否已经通过跟踪带有任何所需ID的标记来实现。为DbContext可能已经加载的标记调用Attach将导致异常。无论是构建一组完整的标记,还是从提供的帖子构建一组标记,解决方案都应该检查DbContext中是否有任何本地缓存/跟踪的标记,并且只附加那些尚未跟踪的标记。

var tags = postViewModels.SelectMany(x => x.TagIds)
    .Distinct()
    .Select(t => new Tag { Id == t))
    .ToList();
foreach(var tag in tags)
{
    if (!context.Tags.Local.Any(x => x.TagId == tag.Id))
        context.Tags.Attach(tag);
}

可能有更好的方法来构建要附加的标记,以排除现有跟踪的标记(例如使用除外的标记,尽管这需要一个平等的比较程序),但我们会防止附加已跟踪的标记。根据第一个示例,我们使用上下文创建帖子并关联所需的标记。标签。本地 。每个帖子中引用的每个标签都应该已经附加或已经跟踪并可用。

这里剩下的警告是,这假设所提供的标记实际上存在于数据库中。我们不想将附加标记的EntityState设置为添加或修改之类的内容,以避免在标记表中创建不完整/无效或替换数据。

 类似资料:
  • 问题内容: 有三个表:,并且,医院能提供的医疗服务和语言服务。因此,存在两个多对多关系。 简单ERD 现在,我想使用和搜索医院数据。 DaoImpl: 而且,如果我想从三个表中按邮政编码,医疗类型和语言进行搜索,那么如何编写一个jsql。 警告: 错误:org.hibernate.hql.internal.ast.ErrorCounter- 预期加入的路径!希望加入的路径!在org.hiberna

  • 我试图让api返回一个注释列表,通过与标签的多对多关系关联,给定一个标签ID。Spring boot自动创建了一个名为notes_tables的桥接表,其中包含一个notes_id字段和一个labels_id字段。Spring Boot还创建了一个notes表和一个labels表。我尝试了以下步骤: 我只需要让它工作,但我特别好奇我是否能让它与jpa方法查询一起工作。任何查询都可以,只要它可以工作

  • 问题内容: 我将Ebean与Play 2 Framework结合使用,并得到了两种模型:用户模型和书本模型。用户模型与书籍模型以一对多关系连接。因此,每个用户可以拥有很多书,甚至根本没有书。书籍模型本身也具有属性。现在,我想在用户模型中创建一个查询,该查询仅返回具有某些属性书的用户。例如:一个属性可能是条件,例如new或used。现在,给我所有拥有新状态书籍的用户。是否可以使用Ebean方法创建这

  • 我是新来laravel的,所以原谅我可能的愚蠢问题。此外,我确实研究了所有其他“类似”的问题,但要么它们没有重现正确的解决方案,要么我真的很难理解。 情景: 我有一个帖子模型和一个主题模型。这就是他们现在的样子。 在岗位上。php 在主题上。php 现在,我需要实现的是: 如果将查询参数传递给请求(即q=Food),我只想返回在主题关系中包含主题食物的帖子,而不返回其他帖子。如果什么都没有通过,那

  • 我正在读MongoDB在行动这本书。我有一个关于的问题。 在中,该书给出了一个多对多关系的示例。它给出了文档和文档。 我理解这里的多对多关联。基本上,可以有一个带有_id等数组的键。所以我不想问类似MongoDB多对多关联的问题 我的问题是关于的,书中给出了关于查询多对多关系的两个示例查询。下面是两个查询: > 是什么意思?我以为只存在于Ruby驱动程序中。 中的是什么?是收藏吗? 中的是什么?

  • 我有一个桌子实验和一个桌子标签。一个实验可能有许多标签。架构: 是否可以使用返回实验和相应标签列表的jooq创建查询? 类似