#c# #odata #asp.net-web-api2
#c# #odata #asp.net-web-api2
Вопрос:
Контроллер:
public class PhaseController: ODataController
{
[EnableQuery]
[System.Web.Http.HttpGet]
public IQueryable < Phase > CustomPhases(CustomPhaseRequest request = null)
{
if (request == null) //default values
request = new CustomPhaseRequest()
{
Duration = 3,
PhaseFilters = new List < CustomPhaseFilter > ()
{
new CustomPhaseFilter()
{
Name = "sp500", DisplayName = "SP500", GreaterThanValue = 10, Method = "returns", FieldName = "BLAH"
},
//new CustomPhaseFilter() { Name= "yr10", DisplayName= "10YR", GreaterThanValue= 0, Method= "delta", FieldName= "BAR", Unit= "bp" },
//new CustomPhaseFilter() { Name= "em", DisplayName= "EM", GreaterThanValue= 0, Method= "returns", FieldName= "FOO" }
}
};
var provider = DrawDownController.YadiYadahDataProvider;
var data = provider.GetCustomPhases(provider.GetCachedData(), request);
return data.AsQueryable();
}
}
Параметр действия контроллера:
public class CustomPhaseRequest
{
public int Duration { get; set; }
public List<CustomPhaseFilter> PhaseFilters { get; set; }
}
public class CustomPhaseFilter
{
public string Name { get; set; } // maps to str
public string DisplayName { get; set; } // maps to display
public double? SmallerThanValue { get; set; } // maps to value
public double? GreaterThanValue { get; set; } // maps to value
public string Method { get; set; } // maps to method
public string FieldName { get; set; } // maps to dataStr
public string Unit { get; set; } // maps to unit
}
Код построения модели:
ODataConventionModelBuilder modelBuilder = new ODataConventionModelBuilder();
EntitySetConfiguration < Phase > phaseConfig = modelBuilder.EntitySet < Phase > ("Phase");
phaseConfig.EntityType.HasKey(p => p.Key);
var phaseType = modelBuilder.EntityType < Phase > ();
var customPhasesFunction = phaseType.Collection.Function("CustomPhases");
customPhasesFunction.ReturnsCollection < Phase > ().Parameter < CustomPhaseRequest > ("request");
IEdmModel model = modelBuilder.GetEdmModel();
config.MapODataServiceRoute("odata", "odata", model);
Из javascript, как бы я вызвал эту функцию odata и передал сложный параметр?
http://example.com/odata/Phase/Default .Пользовательские фазы (@param)
При попытке передать сложный тип в типичном URL-адресе функции odata возникает много проблем. Во-первых, он является частью базового URL, а не частью параметров (после?) итак, если вы взяли полезную нагрузку json, например:
{
"Duration": 3,
"PhaseFilters": [
{
"Name": "sp500",
"DisplayName": "SP500",
"GreaterThanValue": 10,
"Method": "returns",
"FieldName": "BLAH"
},
{
"Name": "yr10",
"DisplayName": "10YR",
"GreaterThanValue": 0,
"Method": "delta",
"FieldName": "BAR",
"Unit": "bp"
},
{
"Name": "em",
"DisplayName": "EM",
"GreaterThanValue": 0,
"Method": "returns",
"FieldName": "FOO"
}
]
}
построил его и urlencoded, даже если я скажу asp.net чтобы игнорировать определенные символы, он по-прежнему не привязывает параметр к фактическому действию контроллера odata, когда я создаю URL-адрес вручную. Это дает 404.
Во-вторых, я понятия не имею, можно ли использовать тип содержимого http GET application / json и параметр данных и поместить его в это местоположение URL, не являющееся параметром, при выполнении вызова jquery ajax.
Семантически это функция odata (а не действие), потому что она ничего не изменяет — просто возвращает данные с пользовательским аргументом, поэтому она использует спецификацию GET (пожалуйста, поправьте меня, если я ошибаюсь).)…
Как это делается?
Ответ №1:
В webapi 2.2 параметры функции и действия могут быть только примитивными типами или типами перечисления.
Если вам нужен сложный тип, вы можете определить тип параметра как string, а затем передать свой параметр как строковый литерал.
Затем в вашем контроллере вы можете получить строковое значение параметра и десериализовать его в объект CustomPhaseRequest.
public IQueryable<Phase> CustomPhases(string requestString)
{
// Deserialize the requestString to requestObject
}
Однако длина URL имеет ограничение. Поэтому, если строка параметра слишком длинная, вы должны определить ее как действие и передать параметр в теле, хотя это ничего не меняет.
Комментарии:
1. Вы уверены в этом? Тогда эта ошибка, которую я получаю, вводит в заблуждение: «Необязательный тип параметра должен быть либо примитивным, сложным, набором примитивов, либо набором сложных».
2. параметр также может быть набором примитивов. Конструктор моделей OData также имеет . Метод CollectionParameter<>() .
Ответ №2:
Вот как я это делаю с помощью связанного действия. Дело в том, что вы должны указать тип odata при использовании сложного объекта в запросе.
// setup model builder (wherever you setup your OData EDM)
var messageAction = builder.EntityType<MessageDto>().Collection.Action("SendMessage");
messageAction.Namespace = "YourNamespace";
messageAction.Parameter<SendMessageRequest>("request");
messageAction.Returns<SendMessageResponse>();
// setup controller action (in controller, duh)
[HttpPost]
[ODataRoute("Messages/YourNamespace.SendMessage")]
public IHttpActionResult SendMessage(ODataActionParameters parameters)
{
// parameter will be in the parameters argument
//...your code
}
// SendMessageRequest
public class SendMessageRequest
{
public Guid FromUserUid { get; set; }
public Guid ToUserUid { get; set; }
public string Message { get; set; }
}
// json request
{
"request": {
"fromUserUid@odata.type": "#Guid",
"fromUserUid": "9A49B9D6-037C-4929-9FB7-0FC627DC9EBD",
"message": "test message",
"toUserUid@odata.type": "#Guid",
"toUserUid": "6BE85BFD-0D3F-487B-9F83-1037D4C35432"
}
}
Ответ №3:
Я разделил свои данные на разные списки (все отсортированные одинаково и одинаковой длины), передал их действию контроллера, а затем воссоздал объект данных, чтобы упростить итерацию. Последний шаг может быть необязательным, для большинства вещей вы также можете просто индексировать непосредственно в различные списки (мне это было нужно для LINQ)
[HttpGet]
public IQueryable<ACTUAL> GetRelatedActuals([FromODataUri] Guid key, ODataActionParameters parameters)
{
var wrIds = parameters["wrIds"] as IEnumerable<Guid?>;
var activities = parameters["activities"] as IEnumerable<string>;
var projs = parameters["projs"] as IEnumerable<string>;
var matchingWrs = new List<Tuple<Guid?, string, string>>();
for (int i = 0; i < wrIds.Count(); i )
{
matchingWrs.Add(new Tuple<Guid?, string, string> ( wrIds.ElementAt(i), activities.ElementAt(i), projs.ElementAt(i)));
}
var acts = _repo.Query<ACTUAL>().Where(act => wrIds.Any(id=>id==act.WorkRequestId) || matchingWrs.Any(wr=>wr.Item3==act.PROJECT_ID amp;amp; wr.Item2==act.ACTIVITY_ID amp;amp; wr.Item1 == null));
return acts;
}
И конфигурация веб-api
var getrelated = builder.EntityType<ACTUAL>()
.Function("GetRelatedActuals");
getrelated.CollectionParameter<Guid>("wrIds");
getrelated.CollectionParameter<string>("activities");
getrelated.CollectionParameter<string>("projs");
getrelated.ReturnsCollection<ACTUAL>();