.NET 6 Entity Framework 核心:多对多关系
.NET 6 Entity Framework Core : many-to-many relationships
我正在使用 .NET 6 和 Entity Framework Core 构建 Web API 以连接到 SQL 服务器数据库。基本的东西看起来可以正常工作,但我无法使模型中的多对多关系正常工作。
考虑数据库中的以下 3 tables:
产品:
类别:
类别产品:
我创建了3个对应这些table的模型如下:
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
class 来与数据库通信:
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
table 中有记录来定义产品和类别之间的关系)。我确定我错过了什么,但我看不出我错过了什么。有人可以为我指出正确的方向来完成这项工作吗?
1.Installing 包裹Microsoft.AspNetCore.Mvc.NewtonsoftJson
2.Change 以下代码来自 builder.Services.AddControllers();
builder.Services.AddControllers().AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
3.In 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();
}
结果:
我在庆果的回答下找到了解决办法。安装 Microsoft.AspNetCore.Mvc.NewtonsoftJson
包并对 Program.cs
和我的 ShopsController.cs
进行建议的更改我能够调用数据库并检索产品及其类别的列表。但是,未检索到类别的 Name
和 Order
属性。
经过反复试验,我想出了一个适合我的解决方案。我对其进行了一些微调以满足我的需要(例如,在此响应中我并不真的需要 Order
属性 来获得 Category
)。我所做的如下:我创建了一些新的 类 来将数据库对象映射到,但没有数据库导航所需的属性。然后我在 DbContext
对象的 Products
table 上的 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
}
]
}
]
它为我提供了特定商店的产品,包括产品所属的类别,并且只显示了该特定商店的价格。这正是我想要的输出结果。
我正在使用 .NET 6 和 Entity Framework Core 构建 Web API 以连接到 SQL 服务器数据库。基本的东西看起来可以正常工作,但我无法使模型中的多对多关系正常工作。
考虑数据库中的以下 3 tables:
产品:
类别:
类别产品:
我创建了3个对应这些table的模型如下:
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
class 来与数据库通信:
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
table 中有记录来定义产品和类别之间的关系)。我确定我错过了什么,但我看不出我错过了什么。有人可以为我指出正确的方向来完成这项工作吗?
1.Installing 包裹Microsoft.AspNetCore.Mvc.NewtonsoftJson
2.Change 以下代码来自 builder.Services.AddControllers();
builder.Services.AddControllers().AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
3.In 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();
}
结果:
我在庆果的回答下找到了解决办法。安装 Microsoft.AspNetCore.Mvc.NewtonsoftJson
包并对 Program.cs
和我的 ShopsController.cs
进行建议的更改我能够调用数据库并检索产品及其类别的列表。但是,未检索到类别的 Name
和 Order
属性。
经过反复试验,我想出了一个适合我的解决方案。我对其进行了一些微调以满足我的需要(例如,在此响应中我并不真的需要 Order
属性 来获得 Category
)。我所做的如下:我创建了一些新的 类 来将数据库对象映射到,但没有数据库导航所需的属性。然后我在 DbContext
对象的 Products
table 上的 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
}
]
}
]
它为我提供了特定商店的产品,包括产品所属的类别,并且只显示了该特定商店的价格。这正是我想要的输出结果。