I was struggling ealier today when having a Web API call in my controller that returns a IQueryable collection from an Entity Framework query:
public IQueryable<Product> GetProductByNameIQueryable(string category)
{
try
{
return _repository.GetProductByNameIQueryable(category);
}
catch
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
}
Error:
<div class="line"><Error></div>
<div class="collapsible-content">
<div class="line"><Message>An error has occurred.</Message></div>
<div class="collapsible" id="collapsible1">
<div class="expanded">
<div class="line"><ExceptionMessage></div>
<div class="collapsible-content">The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'.</div>
<div class="line"></ExceptionMessage></div>
</div>
</div>
<div class="line"><ExceptionType>System.InvalidOperationException</ExceptionType></div>
<div class="line"><StackTrace/></div>
<div class="collapsible" id="collapsible2">
<div class="expanded">
<div class="line"><InnerException></div>
<div class="collapsible-content">
<div class="line"><Message>An error has occurred.</Message></div>
<div class="collapsible" id="collapsible3">
<div class="expanded">
<div class="line"><ExceptionMessage></div>
<div class="collapsible-content">The entity or complex type 'AdventureWorks2012Model1.Product' cannot be constructed in a LINQ to Entities query.</div>
<div class="line"></ExceptionMessage></div>
</div>
</div>
<div class="line"><ExceptionType>System.NotSupportedException</ExceptionType></div>
</div>
<div class="line"></InnerException></div>
</div>
</div>
</div>
<div class="line"></Error></div>
I found returning a List collection worked fine:
public List<Product> GetProductByName(string category)
{
try
{
List<Product> products= _repository.GetProductByName(category);
return products;
}
catch
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
}
However, I am reading bits about OData and how it works with IQueryable collections dynamically. What sort of Web API GET call would you need to be able to serialise this query and pass it back to a client please?
I have created a Web API call in the Controller like this:
public ODataResult<Product> GetWithODataResult(ODataQueryOptions options)
{
var results = (options.ApplyTo(_db.Products) as IQueryable<Product>);
var count = results.Count();
var limitedResults = results.Take(100).ToArray();
return new ODataResult<Product>(results, null, count);
}
The code runs OK but when it passes back to the client code that called it:
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(ConfigurationManager.AppSettings["WebAPI_URL"]);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = client.GetAsync("api/products").Result; // Blocking call!
if (response.IsSuccessStatusCode)
{
var products = response.Content.ReadAsAsync<IEnumerable<Product>>().Result.AsQueryable(); //Error
}
}
The error is:
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.IEnumerable`1[WebFormsWebAPITestFE.Data.Product]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List<T>) that can be deserialized from a
JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.
Path 'Items', line 1, position 9.
I even found myself trying to use MemoryStreams to try and desialise this but that feels like I am doing this wrong even if it worked.
public List<Product> GetWithODataResult(ODataQueryOptions options)
{
var results = (options.ApplyTo(_db.Products) as IQueryable<Product>);
var count = results.Count();
var limitedResults = results.Take(100).ToArray();
return new ODataResult<Product>(results, null, count).ToList();
}
Works perfectly from what I see!
Giving this is up for tonight while I am on a winner
Marked as answer by bigredhf on Dec 16, 2012 05:28 PM
bigredhf
Member
48 Points
81 Posts
Serialise IQueryable collections (using OData)
Dec 16, 2012 03:36 PM|LINK
Hi,
I was struggling ealier today when having a Web API call in my controller that returns a IQueryable collection from an Entity Framework query:
public IQueryable<Product> GetProductByNameIQueryable(string category) { try { return _repository.GetProductByNameIQueryable(category); } catch { throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound)); } }<div class="line"><Error></div> <div class="collapsible-content"> <div class="line"><Message>An error has occurred.</Message></div> <div class="collapsible" id="collapsible1"> <div class="expanded"> <div class="line"><ExceptionMessage></div> <div class="collapsible-content">The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'.</div> <div class="line"></ExceptionMessage></div> </div> </div> <div class="line"><ExceptionType>System.InvalidOperationException</ExceptionType></div> <div class="line"><StackTrace/></div> <div class="collapsible" id="collapsible2"> <div class="expanded"> <div class="line"><InnerException></div> <div class="collapsible-content"> <div class="line"><Message>An error has occurred.</Message></div> <div class="collapsible" id="collapsible3"> <div class="expanded"> <div class="line"><ExceptionMessage></div> <div class="collapsible-content">The entity or complex type 'AdventureWorks2012Model1.Product' cannot be constructed in a LINQ to Entities query.</div> <div class="line"></ExceptionMessage></div> </div> </div> <div class="line"><ExceptionType>System.NotSupportedException</ExceptionType></div> </div> <div class="line"></InnerException></div> </div> </div> </div> <div class="line"></Error></div>I found returning a List collection worked fine:
public List<Product> GetProductByName(string category) { try { List<Product> products= _repository.GetProductByName(category); return products; } catch { throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound)); } }However, I am reading bits about OData and how it works with IQueryable collections dynamically. What sort of Web API GET call would you need to be able to serialise this query and pass it back to a client please?
Any help is much appreciated.
Thanks, Dave.
bigredhf
Member
48 Points
81 Posts
Re: Serialise IQueryable collections (using OData)
Dec 16, 2012 04:07 PM|LINK
I have created a Web API call in the Controller like this:
public ODataResult<Product> GetWithODataResult(ODataQueryOptions options) { var results = (options.ApplyTo(_db.Products) as IQueryable<Product>); var count = results.Count(); var limitedResults = results.Take(100).ToArray(); return new ODataResult<Product>(results, null, count); }The code runs OK but when it passes back to the client code that called it:
using (HttpClient client = new HttpClient()) { client.BaseAddress = new Uri(ConfigurationManager.AppSettings["WebAPI_URL"]); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); HttpResponseMessage response = client.GetAsync("api/products").Result; // Blocking call! if (response.IsSuccessStatusCode) { var products = response.Content.ReadAsAsync<IEnumerable<Product>>().Result.AsQueryable(); //Error } }Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.IEnumerable`1[WebFormsWebAPITestFE.Data.Product]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List<T>) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.
Path 'Items', line 1, position 9.
I even found myself trying to use MemoryStreams to try and desialise this but that feels like I am doing this wrong even if it worked.
bigredhf
Member
48 Points
81 Posts
Re: Serialise IQueryable collections (using OData)
Dec 16, 2012 04:34 PM|LINK
I changed the code on the Web API server to this:
public List<Product> GetWithODataResult(ODataQueryOptions options) { var results = (options.ApplyTo(_db.Products) as IQueryable<Product>); var count = results.Count(); var limitedResults = results.Take(100).ToArray(); return new ODataResult<Product>(results, null, count).ToList(); }Works perfectly from what I see!
Giving this is up for tonight while I am on a winner