如何在 C# Web API 中加入两个表

Posted

技术标签:

【中文标题】如何在 C# Web API 中加入两个表【英文标题】:How to join two table in C# web API 【发布时间】:2021-12-24 23:52:36 【问题描述】:

我是 C# 实体框架的新手。我正在尝试构建 API,但卡在从关系表中检索数据。 我在 MS SQL 数据库中有一个 pei_crops 表,其中 c_id 是主键。我有另一个名为 pei_pests 的表,其中 p_id 是主键。另一个表是 pei_cropspests,我在其中建立了针对哪种害虫袭击哪种作物的关系。多种害虫可以攻击一种作物,一种害虫可以攻击多种作物。在这个 pei_cropspests 表中,我将 p_id 作为主键和外键,将 c_id 作为主键和外键.

pei_crops 表:

c_id c_name c_description
1 Corn NULL

pei_pests 表:

p_id p_name p_URL
1 pest1 NULL
2 pest2 NULL

pei_cropspests 表:

p_id c_id
1 1
2 1

现在我想在我的 API 中展示类似的东西

[

    "cId":1,
    "pests":[
             
               "pId":1,
               "pName": pest1,
               "pURL": null
             ,
             
               "pId":2,
               "pName": pest2,
               "pURL": null
             

           ]

]

到目前为止,我在 C# Web API 项目中的 get 请求如下所示:

[Route("Getspecific/cropId")]
[HttpGet]
public async Task<IActionResult> GetSpecific(int cropId)

    var cropDetails = await _db.PeiCrops.Where(c=>c.CId == cropId).Include(i=>i.PeiCropspests).ToListAsync();
    return Ok(cropDetails);

此代码仅返回影响 cID 编号 1 的害虫的 pID 和 URL。但我还想要害虫名称和 URL 以及它们的 id。

谁能告诉我怎么做。也许有一些方法可以连接两个表并显示数据?我只是不知道如何在 C# 中做到这一点。任何帮助表示赞赏。谢谢。

实体类: PeiCrop:

using System;
using System.Collections.Generic;

#nullable disable

namespace PEI_API.EF

    public partial class PeiCrop
    
        public PeiCrop()
        
            PeiCropimages = new HashSet<PeiCropimage>();
            PeiCropsdiseases = new HashSet<PeiCropsdisease>();
            PeiCropspests = new HashSet<PeiCropspest>();
        

        public int CId  get; set; 
        public string CName  get; set; 
        public string CPhotoUrl  get; set; 
        public string CDescription  get; set; 

        public virtual ICollection<PeiCropimage> PeiCropimages  get; set; 
        public virtual ICollection<PeiCropsdisease> PeiCropsdiseases  get; set; 
        public virtual ICollection<PeiCropspest> PeiCropspests  get; set; 
    

佩佩斯:

using System;
using System.Collections.Generic;

#nullable disable

namespace PEI_API.EF

    public partial class PeiPest
    
        public PeiPest()
        
            PeiCropspests = new HashSet<PeiCropspest>();
            PeiPestimages = new HashSet<PeiPestimage>();
        

        public int PId  get; set; 
        public string PName  get; set; 
        public string PPhotoUrl  get; set; 
        public string PDescription  get; set; 

        public virtual ICollection<PeiCropspest> PeiCropspests  get; set; 
        public virtual ICollection<PeiPestimage> PeiPestimages  get; set; 
    

PeiCropspest:

using System.Collections.Generic;

#nullable disable

namespace PEI_API.EF

    public partial class PeiCropspest
    
        public int PId  get; set; 
        public int CId  get; set; 

        public virtual PeiCrop CIdNavigation  get; set; 
        public virtual PeiPest PIdNavigation  get; set; 
    


【问题讨论】:

也许LINQ可以做到? @urlreader 我很困惑,不知道如何将它与我当前的代码集成并将其返回给 API。 你能展示实体类吗? @Sami 使用此站点学习如何询问 SQL,对现在和将来都非常有用:meta.***.com/questions/333952/… @LeandroBardelli 感谢您的指出。我已经修好了 【参考方案1】:

首先,您需要配置关系:

class MyContext : DbContext

    ...
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    
        base.OnModelCreating(modelBuilder);
        modelBuilder.Entity<PeiCropspest>()
            .HasKey(cp => new  cp.PId, cp.CId );

        //Configure one PeiPest to many PeiCropspest
        modelBuilder.Entity<PeiCropspest>()
            // Specify PeiCropspest's navigation property to one PeiPest
            .HasOne(cp => cp.PIdNavigation)
            // Specify PeiPest's navigaton property to many PeiCropspest
            .WithMany(p => p.PeiCropspests)
            // Specify PeiCropspest's navigation property
            // to use this PeiCropspest's property as foreign key
            .HasForeignKey(cp => cp.PId);

        //Configure one PeiCrop to many PeiCropspest
        modelBuilder.Entity<PeiCropspest>()
            // Specify PeiCropspest's navigation shadow property to one PeiCrop
            .HasOne<PeiCrop>()
            // Specify PeiCrop's navigaton property to many PeiCropspest
            .WithMany(c => c.PeiCropspests)
            // Specify PeiCropspest's navigation shadow property
            // to use this PeiCropspest's property as foreign key
            .HasForeignKey(cp => cp.CId);
    

    public DbSet<PeiCrop> PeiCrops  get; set; 

然后你可以在 LINQ 查询中做一个投影:

public async Task<IActionResult> GetSpecific(int cropId)

    var cropDetails = await _db.PeiCrops
    .Where(c=>c.CId == cropId)
    .Select(c => new 
        cId = c.CId,
        pests = c.PeiCropspests.Select(p => new 
            pId = p.PIdNavigation.PId,
            pName = p.PIdNavigation.PName,
            pUrl = p.PIdNavigation.PPhotoUrl
        )
    )
    .ToListAsync();
    return Ok(cropDetails);

你知道吗?从 EF Core 5 开始,可以在没有中间实体的情况下建立多对多关系。这可以简化您的实体模型。参看。 the documentation

【讨论】:

嘿@vernou 非常感谢你。这个对我有用。 如果这个答案回答了你的问题,你能接受吗? 对不起,这是我的第二篇文章。我不知道答案是否可以接受。接受你的回答【参考方案2】:

你已经很接近了,但你也没有像你一样完全使用 EF,我的意思是你实际上不必自己制作关系表,而是可以直接从实体 pei_crop 引用实体 pei_pests 的列表让 EF 创建另一个。

//Example just getting one property from each, 
//but you can new a composite return type up if you wish, using select
var cropDetails = await _db.PeiCrops
                            .Where(c=>c.CId == cropId)
                            .Include(i=>i.PeiCropspests)
                            .ThenInclucde(t => t.Pests)
                            .Select(s => new  CropId = s.p_id, PestName = s.PeiCropsPests.Pest.p_name  )
                            .ToListAsync();

https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.select?view=net-5.0

【讨论】:

我撤回了我的反对票并支持你,因为示例错误,而不是数据。 Include 在使用自定义 Select 投影时不需要或使用。 嗨@LeandroBardelli 我已将实体类添加到我的问题中。我试图将您提供的代码添加到我的代码中。我遇到了一些错误。因为有些表是不可访问的。就像在 ThenInclude() t.Pests 上向我显示的错误 ICollection 不包含 'Pests' 的定义。也许是因为我没有向您展示实体类,而您试图给我一个想法。也许我需要更改一些代码 @Sami 哦不,答案是为 T. Nielsen 准备的 再次感谢。哈哈。我说错人了。

以上是关于如何在 C# Web API 中加入两个表的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Nhibernate 中加入两个表

如何在 DynamoDb 中加入两个表?

如何在 symfony 5 中加入两个表?

如何在 App Script 中加入两个表并将数据提取到大查询现有表中

如何在 Magento 2.3 中加入两个自定义表

如何在我的 Android 应用程序中加入两个 SQLite 表?