2015-01-18 2 views
0

У меня есть веб-API, где я бы хотел предложить некоторые (ограниченные) запрашиваемые конечные точки для таких вещей, как пейджинг, упорядочение и предварительный выбор результатов (по базе данных: столбцы, включенные в dto). До сих пор все отлично работает, однако у меня много сильно раздутых методов управления с большим количеством технических материалов, и я подумал, есть ли более простой способ для моих требований.Queryable web api

Типичный контроллер выглядит следующим образом:

public HttpResponseMessage GetEntities(int page = MaximumPageIndex, 
    int pageSize = MinimumPageSize, 
    string orderBy = null, 
    [FromUri] EntitySearchCriteria criteria = null) 
{ 

    // ensure that page/pageSize lies within possible boundaries 
    page = page.ToMinimum(MinimumPageIndex); 
    pageSize = pageSize.ToBounds(MinimumPageSize, MaximumPageSize); 

    // create any criteria for entity if none is available 
    criteria = criteria ?? new AnyEntitySearchCriteria(); 

    // create ordering expressions based on string 
    var orderOptions = orderBy != null 
     ? ExpressionBuilder.CreateSelector<Entity, dynamic>(orderBy) 
     : ExpressionBuilder.CreateSelector<Entity, dynamic>("Id"); 

    // create paging options 
    var pagingOptions = new PagingOptions<Entity, dynamic>(page, pageSize, orderOptions); 

    try { 

     // get total count from database 
     var totalCount = _repository.Count(); 

     // get entities by criteria specification (IMemberSpecification<T, TMember>) 
     var results = _repository.GetEntitiesByCriteria(criteria, 
      x = new BlablaDto {} // apply result selectors (not shown in example, 
      // map to paged list 
      pagingOptions).ToPagedList(page, pageSize, totalCount); 

     // create "paged" response with paging http headers (rfc 5988) 
     return Request.CreatePagedResponse(HttpStatusCode.OK, results); 
     ... 
} 

Это, конечно же, снова и снова, для многих предприятий, которые часто выглядят одинаково. Теперь я знаю, что есть Microsoft OData, но я не могу и не буду использовать его по многим причинам (работает только на IQueryable, странные ответы - например: данные подкачки включены в результат json, а не в заголовки HTTP). поэтому, как я могу сохранить простые контроллеры и сократить повторение кода для этой самой задачи?

+0

Ответ лежит на том, как _repository спроектирован и реализован ... Я не вижу его из кода. –

+0

@Werlang Basic CRUD плюс метод GetEntities(), который принимает ISpecification, IQueryOptions (для сортировки/подкачки, который применяется к базовому IQueryable, возвращаемому поставщиком запроса) и выражение для выбора, чтобы вернуть подмножество полей и/или DTO. – xvdiff

+0

этот репозиторий.GetEntities() может быть перемещен в метод шаблона в базовом контроллере ... тогда ваш Controller.GetEntities() реализует всю эту логику и обрабатывает эту конкретную выборку для каждого типа сущности. –

ответ

0

У вашего контроллера много логики, и вы должны подумать о переносе этой логики на другой уровень, такой как слой API-интерфейс/двигатель. Чтобы очистить входные данные, вы можете настроить фильтры поиска с помощью некоторого наследования. Я должен был сделать это во многих последних проектах.

using System.Collections.Generic; 
using System.Runtime.Serialization; 

public interface IPaginatedFilter { 

    int DefaultLimit { get; } 
    int MaximumLimit { get; } 
    List<string> SupportedOrderBy { get; } 
    bool IsAscendingByDefault { get; } 
} 

[DataContract] 
public sealed class PaginationInput 
{ 
    [DataMember(Name = "limit", Order = 2)] 
    public int Limit { get; set; } 

    [DataMember(Name = "offset", Order = 3)] 
    public int Offset { get; set; } 

    [DataMember(Name = "orderBy", Order = 4)] 
    public string OrderBy { get; set; } 

    [DataMember(Name = "orderByDirection", Order = 5)] 
    public string OrderByDirection { get; set; } 

} 

/// <summary> 
///  Base search filter 
/// </summary> 
public class SearchBase { 
    /// <summary> 
    ///  Results page information 
    /// </summary> 
    [DataMember(Name = "page", Order = 1)] 
    public PaginationInput Page { get; set; } 

    /// <summary> 
    ///  Search phrase applicable to any member of the inheriting search filter 
    /// </summary> 
    [DataMember(Name = "any", Order = 1)] 
    public string Any { get; set; } 

} 

public class EntitySearchCriteria : SearchBase, IPaginatedFilter { 

    // columns to provide search functions for 


    #region Implementation of IPaginatedFilter 

    public int DefaultLimit { 
     get { return 20; } 
    } 

    public int MaximumLimit { 
     get { return 50; } 
    } 

    public List<string> SupportedOrderBy { 
     get { return new List<string> 
     { 
      // columns that support ordering 
     }; } 
    } 

    public bool IsAscendingByDefault { 
     get { return true; } 
    } 

    #endregion 

} 

Таким образом, у вас будет тот же API для всех ваших поисковых фильтров. Ваш контроллер действительно должен выглядеть

public HttpResponseMessage GetEntities(
     [FromUri] EntitySearchCriteria filter = null) 
{ 

    // process input using your service layer 

    // generate response 

} 

Если вам необходимо проверить ввод фильтра, а затем отправить на вход в слой проверки перед тем, как обработать запрос. Разделение проблем, особенно на границе вашего приложения, будет в значительной степени окупаться в конечном итоге.

+0

Если вы, как я, вы не знаете, как использовать параметр, такой как ограничение или смещение во время вызова в URL-адресе, форматирование является следующим: http: // myurl/api/Entities? Page [limit] = 30 & page [offset] = 10 – Lithium

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