Получение связанных объектов ASP.NET WebApi OData v4 приводит к "не найден ресурс HTTP, соответствующий URI запроса"
я следовал это 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, и Майк Уоссон указывает, что "asp.net это официальная документация: -)"; Итак, я потратил некоторое время, пытаясь заставить эти связанные объекты работать, думая, что я неправильно настроил проект.
Я думал, что это может иметь какое-то отношение к версии ASP.NET библиотеки OData, которые устанавливал NuGet (консоль NuGet устанавливает 6.9.x, тогда как диалоговое окно NuGet устанавливает 6.5.икс.) Я также задавался вопросом, было ли это потому, что я установил проект как полностью пустой проект, а затем использовал OWIN, поэтому я попробовал его с чистым ASP.NET типовые решения. Я также попробовал несколько других возможных решений: OData-route-attributes на моих методах контроллера; и включая мой уровень данных и модели все в одной библиотеке (у меня они отделены, чтобы оставаться сухими); я даже попытался использовать отладчик маршрута WebApi Риком Андерсоном - Я бы не пытался использовать это снова!
все безрезультатно.
затем я решил, что Майк Уоссон, должно быть, просто пошел по пути наименьшего сопротивления в своем учебнике, и поэтому я вернулся к это так, Вопрос/ответ и изменил его для использования с ODataConventionModelBuilder и повторного использования, как я объясню в моем ответе ниже.
Если кто-нибудь знает более простой способ заставить это работать, пожалуйста, дайте мне знать, в противном случае я рекомендую просто кусать пулю и писать эти Edmmodel-навигации в моем ответе ниже.
2 ответов
как я упоминаю в вопросе, я пробовал много решений, чтобы заставить это работать, но ни один из них не был последовательным в фактическом решении проблемы, и я продолжал избегать решения, изложенного в это так, Вопрос/ответ потому что учебник специально для v4, и я решил, что ответ должен быть для более старой версии (как неразумно).
так что ответ решает проблему, но требует некоторой работы, чтобы вписаться непосредственно в OData v4 и ODataConventionModelBuilder; вот почему я разместили этот вопрос и ответ; предоставить решение, в частности, для OData v4 и ODataConventionModelBuilder, в надежде, что другие не потеряют время, которое я смотрю на это.
сначала настройте EdmModel:
private static IEdmModel GetEdmModel()
{
var builder = new ODataConventionModelBuilder();
builder.EnableLowerCamelCase();
builder.EntitySet<Menu>("Menus");
builder.EntitySet<MenuPermission>("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);
}
наконец, вызов GetEdmModel из WebApiConfig.Регистрация
config.MapODataServiceRoute("odata", null, GetEdmModel());
теперь вызовите one-to-many и many-to-one навигации вашего сервиса OData от вашего клиента, и все должно быть хорошо с твоим миром. В моем случае звонки выглядят так:
один-ко-многим:
http://localhost:19215/Menus(c94f7f98-6987-e411-8119-984be10349a2)/MenuPermissions
многие-к-одному:
http://localhost:19215/MenuPermissions(ba0da52a-6c87-e411-8119-984be10349a2)/Menu
этот ответ предполагает, что вы настроили остальную часть своего проекта так же, как Майк Уоссон предлагает в учебнике, связанном с вопросом (Эта ссылка относится к части 3 - вам нужно будет следуйте Часть 1 первая!).
Я использую ASP.NET 5, веб-API 2.2 и Entity Framework.
другой разработчик, и я также потратил часы, пытаясь выяснить, почему, после того, как тот же учебник в T, мы не могли получить реляционный маршрут, как показано ниже, чтобы вернуть что-либо, кроме 404:
/odata/Supplier(1)/Products
мы также попробовали отладчик маршрута, на который ссылается OP, и он не смог создать ничего, кроме пустого экрана.
к счастью, для наших нужд, один из наших случайных эксперименты работали, и это должно было использовать атрибут ODataRoute следующим образом:
[EnableQuery]
[ODataRoute("Suppliers({key})/Products")]
public IQueryable<Product> GetProductsForSupplier([FromODataUri] int key)
{
...
}