Получение связанных объектов 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)
    {
        ...
    }