You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求.NET6+EF Core+OData8下多对多实体的Get/Post控制器示例

.NET6 + EF Core + OData8 多对多关联控制器示例

基于你提供的实体与DbContext配置,以下是针对Company和Category多对多关联的基础控制器实现,包含Get查询、通过$ref添加/删除关联的核心动作:

一、Company控制器核心代码

using Microsoft.AspNetCore.OData;
using Microsoft.AspNetCore.OData.Routing.Controllers;
using Microsoft.EntityFrameworkCore;
using Reviews.Entities;

namespace Reviews.Controllers
{
    [Route("odata/Companies")]
    [ApiController]
    public class CompaniesController : ODataController
    {
        private readonly YourDbContext _context; // 替换为你的实际DbContext类名

        public CompaniesController(YourDbContext context)
        {
            _context = context;
        }

        // 获取单个公司及关联分类(支持$expand查询)
        [HttpGet("{key}")]
        [EnableQuery]
        public async Task<IActionResult> Get([FromRoute] int key)
        {
            var company = await _context.Companies
                .Include(c => c.Categories)
                .FirstOrDefaultAsync(c => c.CompanyId == key);

            return company == null ? NotFound() : Ok(company);
        }

        // 获取公司的所有关联分类引用($ref集合)
        [HttpGet("{key}/Categories/$ref")]
        [EnableQuery]
        public async Task<IActionResult> GetCategoriesRef([FromRoute] int key)
        {
            var company = await _context.Companies
                .Include(c => c.Categories)
                .FirstOrDefaultAsync(c => c.CompanyId == key);

            if (company == null) return NotFound();

            var categoryRefs = company.Categories?.Select(c => new
            {
                @odata.id = Url.ODataLink(new { controller = "Categories", key = c.CategoryId })
            }) ?? Enumerable.Empty<object>();

            return Ok(categoryRefs);
        }

        // 为公司添加分类关联(通过$ref)
        [HttpPost("{key}/Categories/$ref")]
        public async Task<IActionResult> CreateCategoryRef([FromRoute] int key, [FromBody] ODataActionParameters parameters)
        {
            if (!parameters.TryGetValue("@odata.id", out var odataId))
            {
                return BadRequest("请求体缺少@odata.id字段");
            }

            var categoryKey = Url.ParseODataId(odataId.ToString())?.LastSegment?.GetKey<int>();
            if (categoryKey == null)
            {
                return BadRequest("@odata.id格式无效");
            }

            var company = await _context.Companies.Include(c => c.Categories).FirstOrDefaultAsync(c => c.CompanyId == key);
            var category = await _context.Categories.FindAsync(categoryKey);

            if (company == null || category == null) return NotFound();

            company.Categories ??= new List<Category>();
            if (!company.Categories.Any(c => c.CategoryId == category.CategoryId))
            {
                company.Categories.Add(category);
                await _context.SaveChangesAsync();
            }

            return StatusCode(StatusCodes.Status204NoContent);
        }

        // 删除公司与分类的关联(通过$ref)
        [HttpDelete("{key}/Categories/$ref")]
        public async Task<IActionResult> DeleteCategoryRef([FromRoute] int key, [FromQuery] string id)
        {
            var categoryKey = Url.ParseODataId(id)?.LastSegment?.GetKey<int>();
            if (categoryKey == null)
            {
                return BadRequest("id查询参数格式无效");
            }

            var company = await _context.Companies.Include(c => c.Categories).FirstOrDefaultAsync(c => c.CompanyId == key);
            if (company == null) return NotFound();

            var categoryToRemove = company.Categories?.FirstOrDefault(c => c.CategoryId == categoryKey);
            if (categoryToRemove != null)
            {
                company.Categories.Remove(categoryToRemove);
                await _context.SaveChangesAsync();
            }

            return StatusCode(StatusCodes.Status204NoContent);
        }
    }
}

二、关键配置与操作说明

1. OData端点配置(Program.cs)

确保已启用OData功能并注册实体集:

builder.Services.AddControllers().AddOData(options =>
{
    options.Select().Filter().OrderBy().Expand().Count().SetMaxTop(100);
    options.AddRouteComponents("odata", GetEdmModel());
});

// 构建EDM模型
private static IEdmModel GetEdmModel()
{
    ODataConventionModelBuilder builder = new();
    builder.EntitySet<Company>("Companies");
    builder.EntitySet<Category>("Categories");
    return builder.GetEdmModel();
}

2. $ref操作规范

  • 添加关联请求:POST到odata/Companies/{CompanyId}/Categories/$ref,请求体格式:
    {"@odata.id": "odata/Categories/{CategoryId}"}
    
  • 删除关联请求:DELETE到odata/Companies/{CompanyId}/Categories/$ref?id=odata/Categories/{CategoryId}

3. EF Core多对多处理

你已通过UsingEntity配置了关联表CompanyCategories,EF Core会自动处理关联数据的插入与删除,无需手动操作关联表。

三、Category控制器简化版

若需对Category执行类似的关联操作,控制器结构与CompaniesController一致,只需替换实体类型:

[Route("odata/Categories")]
[ApiController]
public class CategoriesController : ODataController
{
    private readonly YourDbContext _context;

    public CategoriesController(YourDbContext context)
    {
        _context = context;
    }

    [HttpGet("{key}")]
    [EnableQuery]
    public async Task<IActionResult> Get([FromRoute] int key)
    {
        var category = await _context.Categories
            .Include(c => c.Companies)
            .FirstOrDefaultAsync(c => c.CategoryId == key);

        return category == null ? NotFound() : Ok(category);
    }

    // 关联操作方法与CompaniesController逻辑一致,此处省略
}

内容的提问来源于stack exchange,提问作者leadfollowmove

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 13:54:21