Closed VikingsFan closed 5 years ago
I think this issue is resolved and should be closed. It works fine in 7.3.1.
@t03apt yes, that part is fixed, yet still if I do register:
services.TryAddSingleton<ODataUriResolver>(_ => new ODataUriResolver() { EnableCaseInsensitive = true });
...only PascalCase property names (like the names in .NET) will be accepted, no camelCase (like i'd have them on the Angular side).
This is a big issue, cause API's are used by JavaScript, and while .NET uses PascalCase for prop-names, JavaScript uses camelCase. Mixing those 2 is a pain. So this is really a TOP PRIORITY for using OData.
Actually what I don't get that a non Microsoft based OData implementation exists - where this works, wonder how they did it...
@hidegh If you are using .net core and you have .EnableDependencyInjection()
in your code, then try this:
https://github.com/t03apt/AspNetCoreOdataTest/blob/96612ae5fd5fba5f3d38c8378f7dca62e7e59373/AspNetCoreOdataTest/Startup.cs#L54
@hidegh; can you provide a repo of exactly what is not working? i.e., a specific URL that fails to parse based on a given model?
The initial issue should be fixed, and case insensitive shouldn't differentiate between camelCase, PascalCase, or any other case.
@mikepizzo This issue was already solved/fixed. So currently everything works as expected.
I do have a working setup, where any action-result with IQueryable
Startup.cs config:
/*
services.TryAddSingleton(_ =>
{
var mb = new ODataConventionModelBuilder(_, true);
mb.EnableLowerCamelCase();
return mb;
});
*/
services.TryAddSingleton<ODataUriResolver>(_ => new ODataUriResolver() { EnableCaseInsensitive = true });
// Workaround: https://github.com/OData/WebApi/issues/1177
services.AddMvcCore(options =>
{
foreach (var outputFormatter in options.OutputFormatters.OfType<ODataOutputFormatter>().Where(_ => _.SupportedMediaTypes.Count == 0))
{
outputFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/prs.odatatestxx-odata"));
}
foreach (var inputFormatter in options.InputFormatters.OfType<ODataInputFormatter>().Where(_ => _.SupportedMediaTypes.Count == 0))
{
inputFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/prs.odatatestxx-odata"));
}
});
---
app.UseMvc(routes =>
{
routes.MapRoute(
name: "error",
template: "Home/Error/{errorCode?}",
defaults: new { controller = "Home", action = "Error" });
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapRoute(
name: "apiDefault",
template: "api/{controller=Home}/{action=Index}/{id?}");
// Odata
routes.Count().Filter().OrderBy().Expand().Select().MaxTop(null);
// Work - around for #1175
routes.EnableDependencyInjection(odataContainerBuilder =>
{
odataContainerBuilder.AddService(
Microsoft.OData.ServiceLifetime.Singleton,
typeof(ODataUriResolver),
_ => app.ApplicationServices.GetRequiredService<ODataUriResolver>());
});
});
Custom attribute:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNet.OData;
using Microsoft.AspNet.OData.Extensions;
using Microsoft.AspNet.OData.Query;
using System.Linq;
using System;
using Newtonsoft.Json;
using System.Data.Entity.Infrastructure;
using System.Data.Entity;
using System.Collections;
namespace R
{
/// <summary>
/// Replacement for Microsoft.AspNet.OData.EnableQueryAttribute that returns a valid OData response, see: ODataPagedResult.
/// </summary>
public class ODataQueryable : EnableQueryAttribute
{
public override IQueryable ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions)
{
// NOTE: altering OData query:
// https://d-fens.ch/2017/02/26/modifying-odataqueryoptions-on-the-fly/
// https://tutel.me/c/programming/questions/33660648/odata+v4+modify+filter+on+server+side
var baseResult = base.ApplyQuery(queryable, queryOptions);
return baseResult;
}
public override void OnActionExecuted(ActionExecutedContext actionExecutedContext)
{
// NOTE:
// issue https://github.com/OData/WebApi/issues/159
// workaround: https://stackoverflow.com/questions/27545207/items-count-in-odata-v4-webapi-response
base.OnActionExecuted(actionExecutedContext);
// Skip post processing on exceptions...
if (actionExecutedContext.Exception != null)
return;
// ...and on errors
var response = actionExecutedContext.HttpContext.Response;
if (response == null || !response.IsSuccessStatusCode() || actionExecutedContext.Result == null)
return;
//
// Post-process
var responseContent = actionExecutedContext.Result as ObjectResult;
var itemsQueryable = responseContent?.Value as IQueryable;
if (itemsQueryable == null)
throw new NotSupportedException($"Controller action result with OData queries applied has to be an IQueryable<>, returned type is: {responseContent?.Value?.GetType()}!");
// NOTE: if below does not work, we can try: count = long.Parse(Request.Properties["System.Web.OData.TotalCount"].ToString())
var nextLink = actionExecutedContext.HttpContext.Request.ODataFeature().NextLink;
var count = actionExecutedContext.HttpContext.Request.ODataFeature().TotalCount;
//
// Execute (so we are able to catch errors with error attribute)
object items;
var dbQueryable = itemsQueryable as IDbAsyncEnumerable;
if (dbQueryable != null)
{
// EF
var result = itemsQueryable.ToListAsync().Result;
items = result;
}
else
{
// Other...
var elementType = itemsQueryable.ElementType;
var toListMethodInfo = typeof(Enumerable).GetMethod("ToList").MakeGenericMethod(elementType);
var result = toListMethodInfo.Invoke(null, new object[] { itemsQueryable });
items = result;
}
//
// Return
responseContent.Value = new ODataPagedResult((IEnumerable)items, nextLink, count);
}
}
/// <summary>
/// NOTE: theres a PageResult<T> class already in .NET but it does not serialize via JSON as proper OData result...
/// </summary>
public class ODataPagedResult
{
public ODataPagedResult(IEnumerable items, Uri nextLink, long? count)
{
Value = items;
NextLink = nextLink?.ToString() ?? "";
Count = count;
}
[JsonProperty("value")]
public IEnumerable Value { get; set; }
[JsonProperty("@odata.count")]
public long? Count { get; set; }
[JsonProperty("@odata.nextLink")]
public string NextLink { get; set; }
}
}
@hidegh Am I reading this correctly that you no longer have issues with casing and thus this issue can be closed?
@bdebaere correct, it's working now, from that versio 7.0.0 beta or so...
Thanks @hidegh and @bdebaere; closing the issue.
Short summary (3-5 sentences) describing the issue.
Assemblies affected
ODataLib 7.0
https://github.com/OData/odata.net/blob/ODataV4-7.x/src/Microsoft.OData.Core/UriParser/ODataUriParserConfiguration.cs#L50 will always set the property to false after get UriResolver from DI containder : https://github.com/OData/odata.net/blob/ODataV4-7.x/src/Microsoft.OData.Core/UriParser/ODataUriParserConfiguration.cs#L37