【问题标题】:EF Core 5.0 - Updating many-to-many entities in ASP.NET Core Web APIEF Core 5.0 - 更新 ASP.NET Core Web API 中的多对多实体
【发布时间】:2021-05-02 12:44:03
【问题描述】:

在 EF Core 5.0 中引入了多对多关系。我被困在如何通过我的 asp .net api 更新它们。

对于一对一和一对多关系,有一个约定,只需添加属性名称和 ID。

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public BlogImage BlogImage { get; set; }
}

public class BlogImage
{
    public int BlogImageId { get; set; }
    public byte[] Image { get; set; }
    public string Caption { get; set; }

    public int BlogId { get; set; }
    public Blog Blog { get; set; }
}

所以一个合适的 POST 请求可能看起来像

{
  "BlogId": 123,
  "Url": "example.com",
  "BlogImageID": 42
}

但我不知道是否存在约定或多对多关系的外观

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public ICollection<Tag> Tags { get; set; }
}

public class Tag
{
    public string TagId { get; set; }

    public ICollection<Post> Posts { get; set; }
}

是否有约定使用 EF 5.0 将 http 请求的主体映射到多对多关系?

【问题讨论】:

    标签: c# .net entity-framework-core entity-framework-core-5


    【解决方案1】:

    考虑以下两个处于多对多关系的实体-

    public class Post
    {
        public int Id { get; set; }
        public string Title { get; set; }
    
        public ICollection<Tag> Tags { get; set; }
    }
    
    public class Tag
    {
        public int Id { get; set; }
        public string Name { get; set; }
    
        public ICollection<Post> Posts { get; set; }
    }
    

    当更新Post 实体中的Tags 时,在最常见的情况下,从客户端发送标签Ids 的新列表,请求有效负载将如下所示 -

    {
        "id": 123,
        "title": "An Awesome Post",
        "tags": [2, 7, 13]
    }
    

    通常,您希望定义一个 DTO 来表示此请求对象,例如 -

    public class PostUpdateDTO
    {
        public int Id { get; set; }
        public string Title { get; set; }
    
        public List<int> Tags { get; set; }
    }
    

    然后,对于更新操作本身,您可以执行以下操作 -

    [HttpPut]
    public async Task Put([FromBody]PostUpdateDTO dto)
    {
        // fetch existing Post including related Tags
        var post = await _DbCtx.Posts
            .Include(p => p.Tags)
            .FirstOrDefaultAsync(p => p.Id == dto.Post.Id);
    
        // remove all Tags from the existing list
        post.Tags.Clear();
        
        // add new Tags to the list whose Ids are sent by the client
        // but to identify them you need the list of all available tags
        var availableTags = await _DbCtx.Tags.ToListAsync();
        foreach (var id in dto.Tags)
        {
            post.Tags.Add(availableTags.First(p => p.Id == id));
        }
        
        // modify properties of Post if you need, like -
        // post.Title = dto.Title;
    
        await _DbCtx.SaveChangesAsync();
    }
    

    如您所见,这需要访问数据库以获取所有可用Tag 的列表。如果你不喜欢它并想跳过它,你可以尝试以下方法 -

    [HttpPut]
    public async Task Put([FromBody]PostUpdateDTO dto)
    {
        // fetch existing Post including related Tags
        var post = await _DbCtx.Posts
            .Include(p => p.Tags)
            .FirstOrDefaultAsync(p => p.Id == dto.Post.Id);
    
        // remove Tags which are in the existing Tag list, but not 
        // in the new list sent by the client
        post.Tags.Where(tag => !dto.Tags.Any(id => id == tag.Id))
            .ToList().ForEach(tag => post.Tags.Remove(tag));
    
        // add Tags which are in the new list sent by the client, but 
        // not in the existing Tag list
        dto.Tags.Where(id => !post.Tags.Any(tag => tag.Id == id))
            .ToList().ForEach(id => post.Tags.Add(new Tag { Id = id }));
    
        // modify properties of Post if you need, like -
        // post.Title = dto.Title;
    
        await _DbCtx.SaveChangesAsync();
    }
    

    关于 - 属性名称后跟 ID
    您所指的 Id 属性表示外键。这两个实体都不包含外键属性,因为它们都不依赖于另一个。外键意味着父/子或主/从属关系。但是当两个实体是多对多关系时,它们是相互独立的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-17
      • 2021-08-19
      • 2019-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多