Web API, OData, $inlinecount и тестирование

Ранее у меня был контроллер Web API, который выглядел так:

    public IQueryable<ApiDesignOverview> GetList(
        string brandIds = "", 
        string categoryIds = "", 
        string query = "",
        string categoryOp = "or")

Я слышал, что пакет OData NuGet теперь поддерживает параметр OData $ inlinecount, поэтому я попытался добавить его, используя инструкции из http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/supporting-odata-query-options - я не хочу использовать OData оптом, так как это повлечет за собой большую ре-архитектуру приложения, поэтому я остановился на PageResult<T> вариант.

Итак, теперь мой контроллер выглядит так:

    public PageResult<ApiDesignOverview> GetList(
        ODataQueryOptions<ApiDesignOverview> options,
        string brandIds = "", 
        string categoryIds = "", 
        string query = "",
        string categoryOp = "or")

Мои проблемы сейчас:

  • Как мне издеваться над ODataQueryOptions для модульного тестирования?
  • Если их нельзя высмеять, как мне их создать? мне нужно ODataQueryContext построить тот, который требует Microsoft.Data.Edm.IEdmModelчто требует... что? Я не могу найти никакой документации для этого.

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

3 ответа

Решение

Если вы предпочитаете возвращать IQueryable и хотите получить поддержку $inlinecount, это все еще возможно сделать, изменив QueryableAttribute.

public class InlineCountQueryableAttribute : QueryableAttribute
{
    private static MethodInfo _createPageResult =
        typeof(InlineCountQueryableAttribute)
        .GetMethods(BindingFlags.Static | BindingFlags.NonPublic)
        .Single(m => m.Name == "CreatePageResult");

    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        base.OnActionExecuted(actionExecutedContext);

        HttpRequestMessage request = actionExecutedContext.Request;
        HttpResponseMessage response = actionExecutedContext.Response;

        IQueryable result;
        if (response.IsSuccessStatusCode
            && response.TryGetContentValue<IQueryable>(out result))
        {
            long? inlineCount = request.GetInlineCount();
            if (inlineCount != null)
            {
                actionExecutedContext.Response = _createPageResult.MakeGenericMethod(result.ElementType).Invoke(
                    null, new object[] { request, request.GetInlineCount(), request.GetNextPageLink(), result }) as HttpResponseMessage;
            }
        }
    }

    internal static HttpResponseMessage CreatePageResult<T>(HttpRequestMessage request, long? count, Uri nextpageLink, IEnumerable<T> results)
    {
        return request.CreateResponse(HttpStatusCode.OK, new PageResult<T>(results, nextpageLink, count));
    }
}

Обратите внимание, что я использую отражение для создания PageResult. Вместо этого вы можете вернуть понравившийся вам объект, который можно отформатировать с помощью используемого вами средства форматирования. Анонимный объект с результатами и количеством будет работать, если вы используете форматер Json.

Если вы не хотите (или не можете, как в моем случае) отказаться от использования ODataQueryOptions и PageResult, вот как вы можете создать экземпляр ODataQueryOptions для модульных тестов:

//arrange
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/MyProject/api/Customers?$filter=CustomerID eq 1");
var controller = new CustomersController
{
    Request = request
};

ODataModelBuilder modelBuilder = new ODataConventionModelBuilder(); 
modelBuilder.EntitySet<Customer>("Customers"); 
var opts = new ODataQueryOptions<Customer>(new ODataQueryContext(modelBuilder.GetEdmModel(),typeof(Customer)), request);

//act
var result = controller.Get(opts);

//assert
Assert.AreEqual(1, result.Items.First().CustomerID);

В последнем ODataController есть AllowedQueryOptions это решает это.

public class MyOdataController : ODataController

    {
        [Queryable(AllowedQueryOptions = AllowedQueryOptions.All)]
        public IQueryable<Product> Get()
        {
            return Products.AsQueryable();
        }
    }
Другие вопросы по тегам