2015-08-14 2 views
3

Прежде всего, это не дублирование десятков других сообщений, и я пробовал их все, и никто из них не работает.Возвращать выбранные поля только в результатах Web API

У меня есть модель, которая содержит гораздо больше значений, чем мои пользователи, которым требуется веб-ап.

public class Publication 
{ 
    [Key] 
    public int PublicationID { get; set; } 
    public string PublicationTitle { get; set; } 
    public string Frequency { get; set; } 
    public DateTime NextIssueDate { get; set; } 
    public DateTime SpaceDeadline { get; set; } 
    public DateTime MaterialsDeadline { get; set; } 
    public DateTime CreatedDt { get; set; } 
    public string CreatedBy { get; set; } 
    public DateTime UpdatedDt { get; set; } 
    public string UpdatedBy { get; set; } 
} 

Я хочу только сказать несколько полей, которые необходимо передать в API. Я пробовал этот код, но вместо того, чтобы уходить, скажите UpdateBy в результате Json, он возвращает его с нулевым значением. Как я могу избавиться от этого? Я пробовал несколько десятков вариантов, но они либо не компилируются, либо не могут возвращать результаты.

public IQueryable<Publication> GetPublications() 
    { 
     return db.Publications 
      .ToList() 
      .Select(p => new Publication { 
       PublicationID = p.PublicationID, 
       PublicationTitle = p.PublicationTitle, 
       Frequency = p.Frequency, 
       NextIssueDate = p.NextIssueDate 
      }) 
      .AsQueryable(); 
    } 

ответ

6

Это потому, что вы получаете набор Publication объектов таким образом, вы получите каждое свойство, которое содержится в этом классе, будь то заселить вам это или нет. Если вы хотите вернуть подмножество свойств, создайте класс, который имеет только свойства, которые вы хотите вернуть, и создайте экземпляр этого класса в своем запросе.

public IQueryable<WhatIReallyWantToReturn> GetPublications() 
{ 
    return db.Publications 
     .ToList() 
     .Select(p => new WhatIReallyWantToReturn { 
      PublicationID = p.PublicationID, 
      PublicationTitle = p.PublicationTitle, 
      Frequency = p.Frequency, 
      NextIssueDate = p.NextIssueDate 
     }) 
     .AsQueryable(); 
} 

private class WhatIReallyWantToReturn 
{ 
    public int PublicationID { get; set; } 
    public string PublicationTitle { get; set; } 
    public string Frequency { get; set; } 
    public DateTime NextIssueDate { get; set; } 
} 
+0

Да, это то, что мне нужно сделать, создать модель представления. –

+1

Преобразование запрашиваемого в перечислимое (через ToList()), а затем обратно к запросу не имеет смысла. Опустите теги .ToList() и .AsQueryable(), и ваш результат сопоставлен с вашим новым классом непосредственно из db. –

1

, как Крейг В. сказал, что вы можете использовать ViewModel, а также вы можете использовать анонимный тип (уведомление ViewModel это лучший способ, потому что вы можете использовать некоторые утилиты, такие как automapper для автоматического отображения вашего имущества)

+0

. Единственная проблема с анонимным типом - тогда метод не может вернуть 'IQueryable ', он должен будет быть «динамическим». Это будет работать, но это делает модульное тестирование реальной PITA. –

5

Дон» t сериализуйте свой DAO. Создайте полный контракт и затем сериализуйте его выборочно. Для создания разных контрактов для разных случаев вы можете упростить его с помощью Json.Net; вы можете просто создать собственный контракт распознаватель и использовать его в качестве параметра SerializeObject() как так

static void Main(string[] args) 
{ 
    var person = new TestContract {FirstName = "John", LastName = "Doe", Age = 36}; 

    var firstNameContract = new SelectiveSerializer("firstname"); 
    var allPropertiesContract = new SelectiveSerializer("firstname, lastname, age"); 

    var allJson = JsonConvert.SerializeObject(
     person, 
     Formatting.Indented, 
     new JsonSerializerSettings {ContractResolver = allPropertiesContract}); 

    var firstNameJson = JsonConvert.SerializeObject(
     person, 
     Formatting.Indented, 
     new JsonSerializerSettings {ContractResolver = firstNameContract}); 

    Console.WriteLine(allJson); 
    // { 
    // "FirstName": "John", 
    // "LastName": "Doe", 
    // "Age": 36 
    // } 


    Console.WriteLine(firstNameJson); 
    // { 
    // "FirstName": "John",  
    // }   
} 

public class SelectiveSerializer : DefaultContractResolver 
{ 
    private readonly string[] _fields; 

    public SelectiveSerializer(string fields) 
    { 
     var fieldColl = fields.Split(','); 
     _fields = fieldColl 
      .Select(f => f.ToLower().Trim()) 
      .ToArray(); 
    } 

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) 
    { 
     var property = base.CreateProperty(member, memberSerialization); 
     property.ShouldSerialize = o => _fields.Contains(member.Name.ToLower()); 

     return property; 
    } 
} 

public class TestContract 
{ 
    public string FirstName { get; set; } 

    public string LastName { get; set; } 

    public int Age { get; set; } 
} 

без особых усилий, вы могли бы, вероятно, работать это в свой MEDIATYPE по умолчанию форматера (в трубопроводе) искать параметр в запросе, называемый «поля» или что-то еще, а затем использовать пользовательский разрешитель контрактов, если он присутствует, и тогда было бы плавное поведение по умолчанию для ограничения полей, если это указано или сериализует весь объект, если он не указан.

С учётной стороны, это обоснование: Любая модификация данных считается «озабоченностью по просмотру», что означает, что в API она должна контролироваться параметрами запроса и принимать заголовок. В этом случае «представление» данных - application/json, и вы решили «фильтровать» возвращаемые поля. Все это может (и должно быть, imo) обрабатываться во время сериализации. Таким образом, ваша «модель» в этом случае всегда будет полной моделью против некоторого подмножества модели. Полная модель в этом примере содержит имя, фамилию и возраст. На самом деле это могут быть сотни свойств. Если вы хотите разрешить клиенту выбирать подмножество полной модели, вы можете сделать это с помощью выборочной сериализации.

Аналогичные поведении можно выполнять в графике apis. Там по умолчанию для больших моделей является то, что вы получаете пустой объект, если не указываете поля, заставляя клиента быть очень конкретным в отношении того, что он запрашивает, что отлично, когда размер полезной нагрузки имеет значение (например, мобильные приложения). И нет ничего, что могло бы остановить создание полей, таких как «имя», которое могло бы означать «имя, фамилия» или «все», которое включает все свойства.

Я никогда не был поклонником наличия сотен объектов данных, которые обслуживают некоторые специальные требования к набору данных, который используется в 20 различных контекстах, где в некоторых случаях требуется больше данных, а в других - меньше. IMO, если вам нужно пройти один и тот же процесс, чтобы получить данные, независимо от того, завершено это или нет, вы не должны тратить свое время на создание дополнительных объектов для кадрирования данных ради клиента, и это должно помочь вам в этом.

+0

Мне нравится идея вашего 'SelectiveSerializer'; Знаете ли вы, есть ли библиотека, которая это делает? потому что идя дальше с идеей, хотелось бы указать подполя, скажем: 'new SelectiveSerializer (« firstname, mother.firstname, mother.age »);' ... звучит как хороший отдельный проект сам – sports

+1

, очевидно, есть FlexJSON, что имеет метод include и exclude: 'jsons = new JSONSerializer(). include (« name »,« age », ...)' – sports

+0

Json.Net делает это из коробки. Вам просто нужно реализовать свой собственный сериализатор. Это не очень сложно. Я думаю, что в последний раз, когда я это пробовал, это заняло около 5 минут, и большинство из них просто выясняло, что сделал каждый из 3-х методов. Вам не нужно выполнять все это. Я накормил его именами свойств списка и просто сравнил каждый и закорочен, где они не совпадают. – Sinaesthetic

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