2014-11-10 2 views
0

Я использую OData v4 с Web API 2.2.OData v4 - Выберите конкретное поле в объекте

У меня есть сущность, называемая «Лицо» с составными ключами «FirstName» и «LastName». Похоже, это:

public class Person { 
    public string FirstName {get; set;} 
    public string LastName {get; set;} 
    public double Age {get; set;} 
} 

Для поддержки составных ключей, я добавил Ури соглашения поверх установленного по умолчанию, это выглядит следующим образом:

public class CompositeKeyRoutingConvention : EntityRoutingConvention 
{ 
    public override string SelectAction(ODataPath odataPath, HttpControllerContext controllerContext, ILookup<string, HttpActionDescriptor> actionMap) 
    { 
     var action = base.SelectAction(odataPath, controllerContext, actionMap); 

     if (action != null) 
     { 
      var routeValues = controllerContext.RouteData.Values; 
      if (routeValues.ContainsKey(ODataRouteConstants.Key)) 
      { 
       var keyRaw = (string)routeValues[ODataRouteConstants.Key]; 

       var compoundKeyPairs = keyRaw.Split(','); 

       if (!compoundKeyPairs.Any()) 
       { 
        return action; 
       } 

       foreach (var compoundKeyPair in compoundKeyPairs) 
       { 
        var pair = compoundKeyPair.Split('='); 
        if (pair.Length != 2) 
        { 
         continue; 
        } 
        var keyName = pair[0].Trim(); 
        var keyValue = pair[1].Trim(); 

        routeValues.Add(keyName, keyValue); 
       } 
      } 
     } 

     return action; 
    } 

Мой код вызова пытается получить доступ возраст человека, например, так:

http://localhost:46028/Person(firstName='Blah',LastName='Blu')/Age 

Я получаю эту ошибку:

{ "error": { "code": "", "message": "Не найден ресурс HTTP, соответствующий запросу URI 'http: //: 46028/Person (firstName =' Blah ', LastName =' Blu ')/Age '. "," Innererror ": { " message ":" Не найдено соглашения о маршрутизации для выбора действия для пути OData с шаблоном' ~/entityset/key/property '. "," Type ":" », "StackTrace": ""} }}

мой контроллер имеет два метода:

public IQueryable<Person> Get() 
    { 
     return _db.People; 
    } 

    public Person Get([FromODataUri] string firstName, [FromODataUri] string lastName) 
    { 
     var person = _db.People 
      .FirstOrDefault(x => x.FirstName == firstName && x.LastName== lastName); 

     if (person == null) 
     { 
      throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound)); 
     } 

     return person; 
    } 

ответ

0

оказывается, есть простое решение. Web API не поддерживает составные клавиши хорошо, у них есть PropertyRoutingConvention, который выполняет именно то, что я ищу, за исключением того, что он не работает для составных клавиш.

Fixed его, создавая «CompositeKeyPropertyRoutingConvention», приведенные ниже:

public class CompositeKeyPropertyRoutingConvention : PropertyRoutingConvention 
{ 
    public override string SelectAction(ODataPath odataPath, HttpControllerContext controllerContext, ILookup<string, HttpActionDescriptor> actionMap) 
    { 
     var action = base.SelectAction(odataPath, controllerContext, actionMap); 

     return new CompositeKeyRoutingBehaviour().SelectAction(action, odataPath, controllerContext, actionMap); 
    } 
} 

public class CompositeKeyRoutingBehaviour 
{ 
    public string SelectAction(string action, ODataPath odataPath, HttpControllerContext controllerContext, 
           ILookup<string, HttpActionDescriptor> actionMap) 
    { 
     if (action != null) 
     { 
      var routeValues = odataPath.Segments.FirstOrDefault(x => x.SegmentKind == ODataRouteConstants.Key); 

      if (routeValues != null) 
      { 
       var keyRaw = routeValues.ToString(); 

       var formatter = new KeyValueFormatter(); 

       var keyPairs = formatter.FormatRawKey(keyRaw); 

       if (!keyPairs.Any()) 
       { 
        return action; 
       } 

       foreach (var pair in keyPairs) 
       { 
        controllerContext.RouteData.Values.Add(pair.Key, pair.Value); 
       } 
      } 
     } 

     return action; 
    } 
} 

public class KeyValueFormatter 
{ 
    public IDictionary<string, string> FormatRawKey(string rawKey) 
    { 
     var formattedKeys = new Dictionary<string, string>(); 

     if (string.IsNullOrWhiteSpace(rawKey)) 
      return formattedKeys; 

     var keyBuilder = new StringBuilder(); 
     var valueBuilder = new StringBuilder(); 

     var keyBuilding = true; 

     var keys = new List<string>(); 
     var values = new List<string>(); 

     for (var i = 0; i < rawKey.Length; i++) 
     { 
      var currentChar = rawKey[i]; 
      var nextChar = i < rawKey.Length - 1 ? rawKey[i + 1] : (char?)null; 
      var prevChar = i > 0 ? rawKey[i - 1] : (char?)null; 

      if (currentChar == '=' && keyBuilding) 
      { 
       keyBuilding = false; 

       keys.Add(keyBuilder.ToString()); 

       keyBuilder.Clear(); 

       continue; 
      } 

      if (!keyBuilding && currentChar == ',' && prevChar.HasValue && prevChar.Value == '\'') 
      { 
       keyBuilding = true; 

       values.Add(valueBuilder.ToString()); 

       valueBuilder.Clear(); 

       continue; 
      } 

      if (keyBuilding) 
      { 
       keyBuilder.Append(currentChar); 
      } 
      else 
      { 
       valueBuilder.Append(currentChar); 
      } 

      if (!keyBuilding && !nextChar.HasValue) 
      { 
       values.Add(valueBuilder.ToString()); 

       valueBuilder.Clear(); 
      } 
     } 

     if (keys.Count != values.Count) 
     { 
      throw new InvalidDataException("The key could not be formatted into valid pairs. key was: " + rawKey); 
     } 

     for (var i = 0; i < keys.Count; i++) 
     { 
      formattedKeys.Add(keys[i].Trim(), values[i].Trim()); 
     } 

     return formattedKeys; 
    } 
} 

Последнего класс делает некоторый фетиш для обработки «» внутри данных ключей самого, не тестировали с «'» Внутри данных я рекомендую вам это сделать.

+0

На самом деле есть образцы для составного ключа http://aspnet.codeplex.com/SourceControl/latest#Samples/WebApi/OData/v4/ODataCompositeKeySample/. HTH – QianLi

+0

Да, на самом деле код будет очень похож на тот образец, который вы упомянули, однако вопрос был в том, когда я выбираю конкретное поле вместо всего объекта, например. peopleServiceContext.Where (x => x.First .....). Выберите (x => x.Age) ;. Спасибо хоть. –

Смежные вопросы