如何在 C# web API 中加入两个 table

How to join two table in C# web API

我是 C# 新手 entity framework。我正在尝试构建一个 API,但卡在从关系 table.
中检索数据 我在 MS SQL 数据库中有一个 pei_crops table,其中 c_id 是主键。我有另一个 table 叫做 pei_pests,其中 p_id 是主键。另一个 table 是 pei_cropspests,我在其中建立了哪种害虫攻击哪种作物的关系。多种害虫可以攻击一种作物,一种害虫可以攻击多种作物。在这个 pei_cropspests table 我放了 p_id 作为主键和外键,c_id 作为主键和外键。

pei_crops table:

c_id c_name c_description
1 Corn NULL

pei_pests table:

p_id p_name p_URL
1 pest1 NULL
2 pest2 NULL

pei_crops害虫table:

p_id c_id
1 1
2 1

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

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

           ]
}
]

到目前为止,我的 get 请求在 C# web API 项目中看起来像这样:

[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);
}

此代码 returns 我只有影响 cID 号 1 的害虫的 pID 和 URL。但我还想要害虫名称和 URL 及其 ID。

谁能告诉我怎么做。也许有某种方法可以连接两个 table 并显示数据?我只是不知道如何在 C# 中执行此操作。任何帮助表示赞赏。谢谢。

实体class: 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; }
    }
}

你非常接近,但你也没有完全使用 EF,我的意思是你实际上不必自己建立关系 table 但可以直接参考来自实体 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

首先,您需要配置关系:

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);
}

Do you know? From EF Core 5, it's possible to do many to many relationship without intermediary entity. This can simplify your entity model. cf. the documentation