.NET 6实体框架核心:多对多关系

3
我正在使用.NET 6和Entity Framework Core构建Web API,以连接到SQL Server数据库。基本的东西看起来工作正常,但我无法使我的模型中的多对多关系工作。
考虑以下3个表: 产品

enter image description here

分类:

enter image description here

产品类别:

enter image description here

我已经创建了三个与这些表相对应的模型,如下所示:
public class Product
{
    [Key]
    public int Id { get; set; }             = 0;
    public string Name { get; set; }        = string.Empty;
    public string Description { get; set; } = string.Empty;
    public string Label { get; set; }       = string.Empty;
    public int Sativa { get; set; }         = 0;
    public int Indica { get; set; }         = 0;

    public ICollection<CategoryProduct> Categories { get; set; } = new HashSet<CategoryProduct>();
}

public class Category
{
    [Key]
    public int Id { get; set; }      = 0;
    public string Name { get; set; } = string.Empty;
    public int Order { get; set; }   = 0;

    public ICollection<CategoryProduct> Products { get; set; } = new HashSet<CategoryProduct>();
}

public class CategoryProduct
{
    public int CategoryId { get; set; }     = 0;
    public Category? Category { get; set; } = null;
    public int ProductId { get; set; }      = 0;
    public Product? Product { get; set; }   = null;
}

我创建了以下的 DbContext 类来与数据库通信:
public class CoffeeshopContext : DbContext
{
    public DbSet<Shop>? Shops { get; set; }                        = null;
    public DbSet<Category>? Categories { get; set; }               = null;
    public DbSet<Product>? Products { get; set; }                  = null;
    public DbSet<Price>? Prices { get; set; }                      = null;
    public DbSet<CategoryProduct>? ProductCategories { get; set; } = null;

    private readonly IConfiguration _configuration;

    public CoffeeshopContext(DbContextOptions<CoffeeshopContext> options, IConfiguration configuration) : base(options)
    {
        _configuration = configuration;
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<CategoryProduct>().HasKey(x => new { x.CategoryId, x.ProductId });
        modelBuilder.Entity<CategoryProduct>().HasOne(x => x.Product)
                                              .WithMany(x => x.Categories)
                                              .HasForeignKey(x => x.ProductId);
        modelBuilder.Entity<CategoryProduct>().HasOne(x => x.Category)
                                              .WithMany(x => x.Products)
                                              .HasForeignKey(x => x.CategoryId);
        base.OnModelCreating(modelBuilder);
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (!optionsBuilder.IsConfigured)
            optionsBuilder.UseSqlServer(_configuration.GetConnectionString(nameof(CoffeeshopContext)));
    }
}

当我使用Swagger运行项目时,调用检索产品的端点时会得到以下结果:
[
  {
    "id": 3,
    "name": "Ice Cream Bean",
    "description": "Well balanced hybrid with a mood boosting euphoric high and body relaxation. Diesel and citrus aroma with creamy fruity undertones.",
    "label": "CALI STRAIN",
    "sativa": 50,
    "indica": 50,
    "categories": []
  },
  {
    "id": 4,
    "name": "Blue Cheese",
    "description": "Deep relaxing, calming and pain relief. Berry, blue cheese aroma and a creamy taste.",
    "label": "CLASSIC STRAIN",
    "sativa": 20,
    "indica": 80,
    "categories": []
  }
]

因此,端点正在工作并从数据库检索产品。
但是,由于某种原因,它没有与类别建立关系(是的,我确定在ProductCategory表中有记录来定义产品和类别之间的关系)。我确定我缺少了什么,但我不知道我缺少了什么。有人可以指导我如何使其正常工作吗?

当您检索“Products”时,需要使用“.Include(x => x.Categories)”。 - Merna Mustafa
2个回答

3
我在Qing Guo的答案帮助下找到了解决方案。安装了Microsoft.AspNetCore.Mvc.NewtonsoftJson包,并按照建议更改了Program.cs和我的ShopsController.cs,我能够调用数据库并检索产品及其类别列表。然而,类别的NameOrder属性未被检索到。
经过一些试错,我想出了一个适合我的解决方案。我稍微调整了它以满足我的需求(例如,在此响应中,我实际上不需要CategoryOrder属性)。我做的是:创建了一些新类来映射数据库对象,但没有需要用于数据库导航的属性。然后,我在DbContext对象的Products表上添加了一个Select()。最终,我的端点变成了这样:
[HttpGet, Route("~/coffeeshop/products")]
public async Task<ActionResult<IEnumerable<Product>>> GetProducts(int shopid)
{
    if (_context.Products == null)
        return NotFound();

    return await _context.Products
        .Include(x => x.Categories)
        .Include(x => x.Prices)
        .Where(x => x.Prices.Any(y => y.ShopId == shopid))
        .Select(x => new Product()
        {
            Id          = x.Id,
            Name        = x.Name,
            Description = x.Description,
            Label       = x.Label,
            Sativa      = x.Sativa,
            Indica      = x.Indica,
            Categories  = x.Categories.Select(y => new SmallCategory()
            {
                Name  = y.Category.Name,
                Id    = y.CategoryId
            }).ToList(),
            Prices = x.Prices.Where(y => y.ShopId == shopid).Select(z => new SmallPrice()
            {
                Gram1 = z.Gram1,
                Gram2 = z.Gram2,
                Gram5 = z.Gram5
            }).ToList()
        })
        .ToListAsync();
}

这个解决方案产生的响应如下:
[
  {
    "id": 4,
    "name": "Blue Cheese",
    "description": "Deep relaxing, calming and pain relief. Berry, blue cheese aroma and a creamy taste.",
    "label": "CLASSIC STRAIN",
    "sativa": 20,
    "indica": 80,
    "categories": [
      {
        "id": 1,
        "name": "weed"
      },
      {
        "id": 4,
        "name": "indica"
      }
    ],
    "prices": [
      {
        "gram1": 15,
        "gram2": 30,
        "gram5": 67.5
      }
    ]
  }
]

它为我提供了特定商店的商品,包括产品所属的类别,并且只显示该特定商店的价格。这正是我希望输出结果的方式。


1

1. 安装 Microsoft.AspNetCore.Mvc.NewtonsoftJson 包。

2. 将以下代码从 builder.Services.AddControllers(); 改为:

builder.Services.AddControllers().AddNewtonsoftJson(options =>
    options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
); 

3. 在 GetProduct 操作中添加 .Include(x=>x.Categories)

        // GET: api/Products
        [HttpGet]
        public async Task<ActionResult<IEnumerable<Product>>> GetProduct()
        {
          if (_context.Products == null)
          {
              return NotFound();
          }
            return await _context.Products.Include(x=>x.Categories).ToListAsync();
        }

结果:

这里输入图片描述


(注:此内容为HTML代码,无需翻译)

太棒了,它有效了。现在它包括了一个 CategoryProduct 列表,这就是我构建它的方式。然而,我真的希望在 Categories 响应中获取一个包含 NameOrder 属性的 Category 列表。我感觉我在这里漏掉了一些基础知识... - ZiNNED

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接