LINQ提供者对Web API造成了很多悲伤

时间:2016-04-07 21:30:27

标签: c# linq

我正在尝试使用定义良好的模型为定义良好的Web API构建LINQ提供程序。我正在关注这些演练:

在返回单个对象和多个对象时,我很难让它工作。我将解释:

最初在通过id进行查询时,显然返回一个结果它运行得很好但是,当运行一个返回对象集合的查询时,我会得到一个异常。

我做了一些更改,现在它在返回一个对象集合时效果很好,但在按id运行查询时抛出了与以前相同的异常。

我目前拥有的代码(适用于多个结果)如下:

自定义查询提供程序

public override object Execute(Expression expression)
{
    SetSecurityToken();

    var elementType = TypeSystem.GetElementType(expression.Type);
    var elementTypeCollection = typeof (IEnumerable<>).MakeGenericType(elementType);

    var task = GetResult(expression, elementType);
    var resultProperty = typeof(Task<>).MakeGenericType(elementTypeCollection).GetProperty("Result"); 

    //WHEN IF FAILS IT DOES SO HERE!
    var result = resultProperty.GetValue(task);

    return result;
}

private Task GetResult(Expression expression, Type elementType)
{
    var requestUrl = Translate(expression);

    var method = _httpRequest.GetType().GetMethod("GetHttpRequest").MakeGenericMethod(new[] { elementType });
    var task = (Task) method.Invoke(_httpRequest, new object[] { requestUrl, _securityToken, null });

    return task;
}

我有一个包装来自HttpClient的调用的类。 Get方法如下所示:

public async Task<IEnumerable<T>> GetHttpRequest<T>(string apiPath, string token, string contentType = null)
{
    using (var client = GetHttpClient(true, contentType))
    {
        var formattedPath = FormatPathWith(apiPath, token);
        var response = await client.GetAsync(formattedPath);

        var result = response.IsSuccessStatusCode && contentType == null
            ? await response.Content.ReadAsAsync<IEnumerable<T>>()
            : default(IEnumerable<T>);

        return result;
    }
}

如果我删除所有IEnumerable&lt;&gt;引用然后它适用于单个对象结果。它看起来像这样:

查询提供商

public override object Execute(Expression expression)
{
    SetSecurityToken();

    var elementType = TypeSystem.GetElementType(expression.Type);

    var task = GetResult(expression, elementType);
    var resultProperty = typeof(Task<>).MakeGenericType(elementType).GetProperty("Result"); 

    //WHEN IF FAILS IT DOES SO HERE!
    var result = resultProperty.GetValue(task);

    return result;
}

private Task GetResult(Expression expression, Type elementType)
{
    var requestUrl = Translate(expression);

    var method = _httpRequest.GetType().GetMethod("GetHttpRequest").MakeGenericMethod(new[] { elementType });
    var task = (Task) method.Invoke(_httpRequest, new object[] { requestUrl, _securityToken, null });

    return task;
}

HTTP包装器

public async Task<T> GetHttpRequest<T>(string apiPath, string token, string contentType = null)
{
    using (var client = GetHttpClient(true, contentType))
    {
        var formattedPath = FormatPathWith(apiPath, token);
        var response = await client.GetAsync(formattedPath);

        var result = response.IsSuccessStatusCode && contentType == null
            ? await response.Content.ReadAsAsync<T>()
            : default(T);

        return result;
    }
}

在这两种情况下抛出的异常是:

  

{&#34;无法将当前JSON数组(例如[1,2,3])反序列化为类型   &#39; LinqProvider.Model.Project&#39;因为类型需要JSON对象   (例如{\&#34; name \&#34;:\&#34; value \&#34;})要正确反序列化。解决这个问题   错误要么将JSON更改为JSON对象(例如,   {\&#34; name \&#34;:\&#34; value \&#34;})或将反序列化类型更改为数组或   实现集合接口的类型(例如ICollection,IList)   像可以从JSON数组反序列化的List。   JsonArrayAttribute也可以添加到类型中以强制它   从JSON数组反序列化。路径&#39;&#39;,第1行,第1位。&#34;}

我正在使用JSON.Net来处理json序列化,但我不认为它可能与此相关,因为我不需要更改对象的属性来查看行为的差异,当然我可以错了。

感谢您的帮助。

1 个答案:

答案 0 :(得分:0)

结果证明问题在于,根据从Web API检索对象的方式,JSON将是单个对象或对象数组,并且在查询提供程序上显示此行:

var elementType = TypeSystem.GetElementType(expression.Type);

表示硬编码为IEnumerable或T的对象的预期类型。

幸运的是,从已翻译的表达式(要调用的URL)中我可以确定可以预期的内容,例如,如果搜索是通过'Id'完成的,我可以期待单个对象或者任何单词'all' ,'搜索','拥有'被发现是一个集合是可以预期的。

所以最终解决方案看起来像这样:

public override object Execute(Expression expression)
        {
            var translatedExpression = Translate(expression);
            SetAdminSecurityToken();

            var elementType = GetElementType(expression);

            var task = GetResultFrom(elementType, translatedExpression);
            var resultProperty = typeof(Task<>).MakeGenericType(elementType).GetProperty("Result"); 
            var result = resultProperty.GetValue(task);

            return GetReturnValue(result, elementType);
        }

    private Type GetElementType(Expression expression)
    {
        var elementType = TypeSystem.GetElementType(expression.Type);
        //The IsMultipleResults property on the translator encapsulates the
        //logic mentioned in the answer description.
        return _queryTranslator.IsMultipleResults ? typeof(IEnumerable<>).MakeGenericType(elementType) : elementType;
    }

        private Task GetResultFrom(Type elementType, string translatedExpression)
        {
            var securityToken = _queryTranslator.UseUserToken
                ? GetSecurityToken(_queryTranslator.UserEmail, _queryTranslator.UserPassword)
                : _securityToken;

            var method = _httpRequest.GetType().GetMethod("GetHttpRequest").MakeGenericMethod(new[] { elementType });
            var task = (Task)method.Invoke(_httpRequest, new object[] { translatedExpression, securityToken, null });

            return task;
        }

在这种情况下,我可以根据翻译过的URL中的一些关键词确定类型,但重要的是,只要可以通过某种方式确定,我可以将预期类型设置为单个T或IEnumerable。 / p>

希望这对某些人有用。