Пожалуйста, мне нужна помощь в выяснении того, как предоставить табличную функцию как свойство объекта, установленного в службе OData v4 Web Api 2.
В моей упрощенной схеме есть три таблицы: Structures, Locations и LocationLinks. Структура содержит граф с узлами (Locatons) и ребрами (LocationLinks). Я обращаюсь к первой модели базы данных Entity Framework 6.
Simplified Schema
Structure:
ID
Locations:
ID
ParentID -> Structure
LocationLinks
A -> Location
B -> Location
Цель состоит в том, чтобы получить доступ к коллекции структур LocationLink так же, как я получаю доступ к местоположению структуры. то есть, чтобы запросить график структуры №180:
http://.../OData/Structures(180)/LocationLinks
http://.../OData/Structures(180)/Locations
Запрос Locations работает автоматически, но я не могу понять, как добавить правильный маршрут для включения запроса LocationLinks. Думая, что это упростит мою задачу, я добавил на свой SQL-сервер табличную функцию. Функция присутствует в моей модели EF и возвращает коллекцию объектов LocationLink:
StructureLocationLinks(@StructureID) -> LocationLinks
К сожалению, что бы я ни пытался, мне кажется, что URL-адрес Structure (180) \ LocationLinks не работает. Это моя последняя попытка:
Фрагмент StructuresController.cs:
// GET: odata/Structures(5)/Locations
[EnableQuery]
public IQueryable<Location> GetLocations([FromODataUri] long key)
{
return db.Structures.Where(m => m.ID == key).SelectMany(m => m.Locations);
}
// GET: odata/Structures(5)/LocationLinks
[EnableQuery]
//[System.Web.OData.Routing.ODataRoute("Structures({key})")]
public IQueryable<LocationLink> GetLocationLinks([FromODataUri] long key)
{
return db.StructureLocationLinks(key);
}
Фрагмент WebApi.cs:
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.UseDataContractJsonSerializer = true;
//json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.All;
//var cors = new System.Web.Http.Cors.EnableCorsAttribute("*", "*", "*");
//config.EnableCors(cors);
// Web API routes
config.MapHttpAttributeRoutes();
ODataConventionModelBuilder builder = GetModel();
config.MapODataServiceRoute(routeName: "odata",
routePrefix: null,
model: builder.GetEdmModel());
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
public static ODataConventionModelBuilder GetModel()
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.Namespace = typeof(Structure).Namespace;
AddLocationLinks(builder);
AddStructures(builder);
builder.EntitySet<Location>("Locations");
return builder;
}
public static void AddStructures(ODataModelBuilder builder)
{
var structSetconfig = builder.EntitySet<Structure>("Structures");
var structConfig = structSetconfig.EntityType;
var functionConfig = structConfig.Collection.Function("StructureLocationLinks");
functionConfig.Parameter<long>("StructureID");
functionConfig.Returns<LocationLink>();
}
public static void AddLocationLinks(ODataModelBuilder builder)
{
var type = builder.EntityType<LocationLink>();
type.HasKey(sl => sl.A);
type.HasKey(sl => sl.B);
builder.EntitySet<LocationLink>("LocationLinks");
}
Я получаю следующую ошибку:
{"error": {"code": "", "message": "Не найден HTTP-ресурс, соответствующий URI запроса 'http://.../OData/Structures(180)/LocationLinks '.", "innererror": {"message": "Не найдено соглашение о маршрутизации для выбора действия для пути OData с шаблоном '~ / entityset / key / unresolved'. "," type ":" "," stacktrace ":" "}}}
Основываясь на некоторых поисках, я попытался добавить к контроллеру атрибут ODataRoute:
// GET: odata/Structures(5)/LocationLinks
[EnableQuery]
[System.Web.OData.Routing.ODataRoute("Structures({key})/LocationLinks")]
public IQueryable<LocationLink> GetLocationLinks([FromODataUri] long key)
{
return db.StructureLocationLinks(key);
}
Что приводит к этой ошибке:
Шаблон пути Structures ({key}) / LocationLinks в действии GetLocationLinks в контроллере Structures не является допустимым шаблоном пути OData. Обнаружен неразрешенный сегмент пути "LocationLinks" в шаблоне пути OData "Structures ({key}) / LocationLinks".
Как я могу сделать доступными LocationLinks из коллекции Structures? Спасибо за уделенное время.
Изменить:
Мне удалось заставить это работать после того, как я нашел этот вопрос: Добавление пользовательского свойство навигации с поддержкой запроса к ODataConventionModelBuilder
Мне пришлось вызвать .GetEdmModel для моего объекта ODataConventionBuilder, а затем добавить свойство навигации в модель с помощью этой функции:
private static Microsoft.OData.Edm.IEdmModel AddStructureLocationLinks(IEdmModel edmModel)
{
var structures = edmModel.EntityContainer.FindEntitySet("Structures") as EdmEntitySet;
var locationLinks = edmModel.EntityContainer.FindEntitySet("LocationLinks") as EdmEntitySet;
var structType = structures.EntityType() as EdmEntityType;
var locLinksType = locationLinks.EntityType() as EdmEntityType;
var structLocLinksProperty = new EdmNavigationPropertyInfo();
structLocLinksProperty.TargetMultiplicity = Microsoft.OData.Edm.EdmMultiplicity.Many;
structLocLinksProperty.Target = locLinksType;
structLocLinksProperty.ContainsTarget = true;
structLocLinksProperty.OnDelete = Microsoft.OData.Edm.EdmOnDeleteAction.None;
structLocLinksProperty.Name = "LocationLinks";
var navigationProperty = structType.AddUnidirectionalNavigation(structLocLinksProperty);
structures.AddNavigationTarget(navigationProperty, locationLinks);
return edmModel;
}
Проблема, с которой я столкнулся сейчас, заключается в том, что у меня ограниченная возможность доступа к свойству навигации в запросах. Например, эта ссылка работает:
http://.../OData/Structures(180)/Children?$expand=Locations
Пока этого нет.
http://.../OData/Structures(180)/Children?$expand=LocationLinks
Возвращенная ошибка
{"error": {"code": "", "message": "Произошла ошибка.", "innererror": {"message": "Свойство экземпляра 'LocationLinks' не определено для типа 'ConnectomeDataModel.Structure' "," тип ":" System.ArgumentException "," stacktrace ":" в System.Linq.Expressions.Expression.Property (выражение выражения, String propertyName) \ r \ n в System.Web.OData.Query.Expressions.SelectExpandBinder .CreatePropertyValueExpressionWithFilter (IEdmEntityType elementType, свойство IEdmProperty, источник выражения, FilterClause filterClause) \ r \ n в System.Web.OData.Query.Expressions.SelectExpandBinder.BuildPropertyContainer (IEdmEntityType_элемент_EdmEntityType) (IEdmEntityType_элемент_элемент_IdmEntity1 \ n в System.Web.Http.Controllers.ActionFilterResult.d__2.MoveNext () \ r \ n --- Конец трассировки стека из предыдущего места, где было сгенерировано исключение --- \ r \ n в System.Runtime.CompilerServices. TaskAwaiter.ThrowForNonSuccess (Задача задача) \ r \ n в System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (Task task) \ r \ n в System.Runtime.CompilerServices.TaskAwaiter`1.GetResult () \ r \ n в System.Web.Http.DispatcherControl.Http. d__1.MoveNext () "}}}