ASP.NET Core OData 8如何支持客户端发起的深度导航请求?
问题:如何在OData API中支持深度导航请求
背景信息
我们使用odata2ts作为客户端库,其文档说明:OData会暴露入口点(通常是实体集,比如/People),可以通过键定位到特定实体,再通过导航属性进行无限深度的导航(例如/People('russelwhyte')/BestFriend/Trips)。
我们通过EF Core Power Tools以数据库优先模式(基于SQL Server 2012)生成了EF实体类,已经在EDM中注册为复数名称的实体集,实体类代码如下:
public class Service { int ServiceId { get; set; } ICollection<ServiceArticle> ServiceArticle { get; set; } } public class ServiceArticle { int ServiceArticleId { get; set; } int ServiceId { get; set; } Service Service { get; set; } int ArticleId { get; set; } Article Article { get; set; } } public class Article { int ArticleId { get; set; } }
odata2ts客户端可发起如下调用:
var article = await odata2tsService.Services(15).ArticleService(20).Article().query();
该调用会转换为OData请求:
/Services(15)/ArticleService(20)/Article
当前配置与问题
我们已按照OData约定配置了端点,例如在ServicesController中有:
[HttpGet] [EnableQuery] public IActionResult GetArticleService([FromRoute] int key)
同时各相关实体的控制器中也配置了Get()和Get([FromRoute] int key)方法。但添加如下方法后仍无法支持上述请求:
[HttpGet] [EnableQuery] public IActionResult GetArticleFromService([FromRoute] int key, [FromRoute] int relatedKey)
需要补充的配置
1. 确保EDM模型正确配置导航关系
在构建EDM模型时,需显式确认导航属性的关联配置,避免约定自动识别失效:
var builder = new ODataConventionModelBuilder(); builder.EntitySet<Service>("Services"); builder.EntitySet<ServiceArticle>("ServiceArticles"); builder.EntitySet<Article>("Articles"); // 显式配置外键与导航关系 builder.EntityType<Service>() .HasMany(s => s.ServiceArticle) .WithOne(sa => sa.Service) .HasForeignKey(sa => sa.ServiceId); builder.EntityType<ServiceArticle>() .HasOne(sa => sa.Article) .WithMany() .HasForeignKey(sa => sa.ArticleId);
2. 遵循OData导航属性的路由方法命名规则
OData路由依赖特定的方法命名格式来匹配深度导航请求,需按以下方式配置控制器方法:
在ServicesController中处理Service到单个ServiceArticle的导航
// 匹配 /Services({key})/ServiceArticle({relatedKey}) [HttpGet("Services({key})/ServiceArticle({relatedKey})")] [EnableQuery] public IActionResult GetServiceArticle([FromRoute] int key, [FromRoute] int relatedKey) { var serviceArticle = _context.ServiceArticles .FirstOrDefault(sa => sa.ServiceId == key && sa.ServiceArticleId == relatedKey); return serviceArticle == null ? NotFound() : Ok(serviceArticle); }
在ServiceArticlesController中处理ServiceArticle到Article的导航
// 匹配 /ServiceArticles({key})/Article [HttpGet("ServiceArticles({key})/Article")] [EnableQuery] public IActionResult GetArticleFromServiceArticle([FromRoute] int key) { var serviceArticle = _context.ServiceArticles .Include(sa => sa.Article) .FirstOrDefault(sa => sa.ServiceArticleId == key); if (serviceArticle == null || serviceArticle.Article == null) { return NotFound(); } return Ok(serviceArticle.Article); }
3. 修正客户端与API的命名一致性
客户端生成的请求中使用了ArticleService,但实体类中的导航属性是ServiceArticle,这会导致路由匹配失败。需检查:
- EDM模型中
Service的导航属性名称是否为ServiceArticle - 调整odata2ts的生成配置,确保客户端导航属性名称与API端一致
4. 确保OData端点启用完整查询功能
在配置端点时,需启用OData的核心查询功能,保证深度导航请求被正确解析:
app.UseEndpoints(endpoints => { endpoints.MapControllers(); endpoints.MapODataRoute("odata", "odata", GetEdmModel()); // 启用OData查询选项 endpoints.Select().Filter().OrderBy().Count().MaxTop(100); });
内容的提问来源于stack exchange,提问作者Juncal
相关产品推荐
相关产品推荐

