获取相关实体ASP.NET WebApi OData v4导致“未找到与请求URI匹配的HTTP资源”

我按照Mike Wasson的这个asp.net教程 ,设法建立相关实体就好了,但是当我将这个逻辑应用到我的项目中时,更复杂的实体关系(其中有更多的实体关系;这是唯一的区别)在OData调用中不会成功,我得到了404这个有效负载:

{ "error": { "code": "", "message": "No HTTP resource was found that matches the request URI 'http://localhost:19215/Menus(c94f7f98-6987-e411-8119-984be10349a2)/MenuPermissions'.", "innererror": { "message": "No routing convention was found to select an action for the OData path with template '~/entityset/key/unresolved'.", "type": "", "stacktrace": "" } } } 

该教程没有提到必须设置EdmModel导航,Mike Wasson指出“asp.net是官方文档:-)”; 所以,我花了一些时间试图让这些相关的实体工作,以为我错误地设置了项目。

我认为它可能与NuGet正在安装的ASP.NET OData库版本有关(NuGet Console安装6.9.x,而NuGet Dialog安装6.5.x)。 我也想知道是不是因为我将项目设置为一个完全空的项目然后使用OWIN,所以我尝试使用纯ASP.NET模板化解决方案。 我还尝试了一些其他可能的解决方案:我的控制器方法上的OData-route-attributes; 并将我的数据层和模型都包含在同一个库中(我将它们分开以保持DRY); 我甚至试图使用Rick Anderson的WebApi路由调试器 – 我不会再尝试使用它!

一切都无济于事。

有一段时间他们工作,但我不知道为什么; 他们停止了下一次建造/运行的工作 – 我想我之间改变了一些东西,但它很小,我在每一步都失去了信心。

然后我决定Mike Wasson必须在他的教程中采取阻力最小的路径,所以我回复到这个问题/答案并修改它以便与ODataConventionModelBuilder一起使用并重用,我将在下面的答案中解释。

如果有人知道更简单的方法让这个工作,请告诉我,否则我建议只是咬住子弹并在下面的答案中写下这些EdmModel-Navigations。

正如我在问题中提到的那样,我尝试了许多解决方案来实现这一点,但没有一个在实际解决问题方面是一致的,并且我一直避免在这个SO问题/答案中提出的解决方案,因为该教程专门针对v4而我想通了答案必须是旧版本(多么不明智)。

所以答案确实解决了问题,但需要一些工作才能直接适应OData v4和ODataConventionModelBuilder; 这就是我发布这个问题和答案的原因; 提供解决方案,特别是OData v4和ODataConventionModelBuilder,希望其他人不会失去我所研究的时间。

首先,设置您的EdmModel:

 private static IEdmModel GetEdmModel() { var builder = new ODataConventionModelBuilder(); builder.EnableLowerCamelCase(); builder.EntitySet("Menus"); builder.EntitySet("MenuPermissions"); var edmModel = builder.GetEdmModel(); AddNavigations(edmModel); //see below for this method return edmModel; } 

第二个AddNavigations:

 private static void AddNavigations(IEdmModel edmModel) { AddMenuPermissionsNavigation(edmModel); } private static void AddMenuPermissionsNavigation(IEdmModel edmModel) { var menus = (EdmEntitySet) edmModel.EntityContainer.FindEntitySet("Menus"); var menuPermissions = (EdmEntitySet)edmModel.EntityContainer.FindEntitySet("MenuPermissions"); var menuType = (EdmEntityType) edmModel.FindDeclaredType("iiid8.cms.data.models.Menu"); //"iiid8.cms.data.models" is the C# namespace var menuPermissionType = (EdmEntityType)edmModel.FindDeclaredType("iiid8.cms.data.models.MenuPermission"); //as above, "iiid8.cms.data.models" is the C# namespace AddOneToManyNavigation("MenuPermissions", menus, menuPermissions, menuType, menuPermissionType); AddManyToOneNavigation("Menu", menus, menuPermissions, menuType, menuPermissionType); } private static void AddOneToManyNavigation(string navTargetName, EdmEntitySet oneEntitySet, EdmEntitySet manyEntitySet, EdmEntityType oneEntityType, EdmEntityType manyEntityType) { var navPropertyInfo = new EdmNavigationPropertyInfo { TargetMultiplicity = EdmMultiplicity.Many, Target = manyEntityType, ContainsTarget = false, OnDelete = EdmOnDeleteAction.None, Name = navTargetName }; oneEntitySet.AddNavigationTarget(oneEntityType.AddUnidirectionalNavigation(navPropertyInfo), manyEntitySet); } private static void AddManyToOneNavigation(string navTargetName, EdmEntitySet oneEntitySet, EdmEntitySet manyEntitySet, EdmEntityType oneEntityType, EdmEntityType manyEntityType) { var navPropertyInfo = new EdmNavigationPropertyInfo { TargetMultiplicity = EdmMultiplicity.One, Target = oneEntityType, ContainsTarget = false, OnDelete = EdmOnDeleteAction.None, Name = navTargetName }; manyEntitySet.AddNavigationTarget(manyEntityType.AddUnidirectionalNavigation(navPropertyInfo), oneEntitySet); } 

最后,从WebApiConfig.Register调用GetEdmModel

 config.MapODataServiceRoute("odata", null, GetEdmModel()); 

现在,从您的客户端调用您的OData服务的一对多和多对一导航,所有这些都应该对您的世界有益。 在我的情况下,调用看起来像这样:

一到多:

 http://localhost:19215/Menus(c94f7f98-6987-e411-8119-984be10349a2)/MenuPermissions 

多对一之一:

 http://localhost:19215/MenuPermissions(ba0da52a-6c87-e411-8119-984be10349a2)/Menu 

这个答案假定您设置了项目的其余部分,就像Mike Wasson在问题中链接的教程中所建议的那样(该链接指向第3部分 – 您将需要首先遵循第1部分 !)。

我正在使用ASP.NET 5,Web API 2.2和entity framework。

另一个开发人员和我也花了几个小时试图弄清楚为什么,在跟随相同的教程到T之后,我们无法得到如下的关系路由来返回404以外的任何东西:

 /odata/Supplier(1)/Products 

我们还尝试了OP中引用的路由调试器,它无法生成除空白屏幕之外的任何内容。

幸运的是,根据我们的需求,我们的一个随机实验工作,那就是使用ODataRoute属性,如:

  [EnableQuery] [ODataRoute("Suppliers({key})/Products")] public IQueryable GetProductsForSupplier([FromODataUri] int key) { ... }