Open TooMuchLagWillKillYou opened 8 months ago
Please use the "Discussions" feature for asking questions.
@TooMuchLagWillKillYou 'StringAsEnumResolver' should work for string enum value used in $filter, but not for integer value.
You can do it by customizing a resolver. Here's my version:
public class MyResolver : UnqualifiedODataUriResolver
{
private StringAsEnumResolver enumResolver = new StringAsEnumResolver();
public MyResolver()
{
EnableCaseInsensitive = true;
}
public override void PromoteBinaryOperandTypes(BinaryOperatorKind binaryOperatorKind, ref SingleValueNode leftNode, ref SingleValueNode rightNode, out IEdmTypeReference typeReference)
{
typeReference = null;
if (leftNode.TypeReference.IsEnum() && rightNode.TypeReference.IsInt32() && rightNode is ConstantNode)
{
string text = (((ConstantNode)rightNode).Value).ToString();
ODataEnumValue val;
IEdmTypeReference typeRef = leftNode.TypeReference;
if (TryParseEnum(typeRef.Definition as IEdmEnumType, text, out val))
{
rightNode = new ConstantNode(val, text, typeRef);
return;
}
}
else if (rightNode.TypeReference.IsEnum() && leftNode.TypeReference.IsInt32() && leftNode is ConstantNode)
{
string text = ((ConstantNode)leftNode).Value.ToString();
ODataEnumValue val;
IEdmTypeReference typeRef = rightNode.TypeReference;
if (TryParseEnum(typeRef.Definition as IEdmEnumType, text, out val))
{
leftNode = new ConstantNode(val, text, typeRef);
return;
}
}
enumResolver.PromoteBinaryOperandTypes(binaryOperatorKind, ref leftNode, ref rightNode, out typeReference);
}
private static bool TryParseEnum(IEdmEnumType enumType, string value, out ODataEnumValue enumValue)
{
long parseResult;
bool num = enumType.TryParseEnum(value, ignoreCase: true, out parseResult);
enumValue = null;
if (num)
{
enumValue = new ODataEnumValue(parseResult.ToString(CultureInfo.InvariantCulture), enumType.FullTypeName());
}
return num;
}
}
And register it in program.cs as:
services.AddControllers().AddOData(opt =>
{
opt
.Count().Filter().Expand().Select().OrderBy().SetMaxTop(5)
.AddRouteComponents("odata", GetEdmOdataModel(), services => services.AddSingleton<ODataUriResolver, MyResolver>())
;
});
Then, it should work:
http://localhost:50000/odata/Authors?$filter=Kind eq TestOdata.Models.RoleKind'Poster'
(it's by default)http://localhost:50000/odata/Authors?$filter=Kind eq 2
(it's using MyResolver)http://localhost:50000/odata/Authors?$filter=Kind eq '2'
(It's using StringAsEnumResolver)http://localhost:50000/odata/Authors?$filter=Kind eq 'Poster'
(It's using StringAsEnumResolver)All four requests return the following same payload:
@mikepizzo I am thinking it might be helpful for customer to use interger to do enum filter by adding the above resolver logic directly into ODL.
@mikepizzo I am thinking it might be helpful for customer to use interger to do enum filter by adding the above resolver logic directly into ODL.
I think that would be amazing. I'd call it IntOrStringAsEnumResolver
and I would inject the existing StringAsEnumResolver
in the constructor instead of managing an internal instance.
One thing I will mention regarding this behavior is that, IMHO, anything that works different than the standard AspNetCore enum handling is only going to cause unnecessary confusion and frustration.
Ideally, OData should not only replicate the behavior, but leverage it. I understand that may not be possible though but figured I'd throw it out there as it would be something I'd investigate if working on this (even if it required working with the AspNetCore team for a solution).
@mikepizzo I am thinking it might be helpful for customer to use interger to do enum filter by adding the above resolver logic directly into ODL.
For comparisons, by default, we should support comparisons to either member names or values. However, note that, according to OData Protocol, either is supposed to be quoted:
enum = [ qualifiedEnumTypeName ] SQUOTE enumValue SQUOTE enumValue = singleEnumValue *( COMMA singleEnumValue ) singleEnumValue = enumerationMember / enumMemberValue enumMemberValue = int64Value
That said, I don't know that I would be opposed to supporting what amounts to an implicit cast from the numeric enum value to the string numeric value.
, services => services.AddSingleton<ODataUriResolver, MyResolver>()
I just want to point out that this solves my issue.
I know that in previous versions of OData we could filter a collection using the numeric representation of an enum, like in this example:
But now this seems to be impossible to do since I get this error:
I have been looking for documentation on the topic for the past two days and couldn't find a single line of code that was helpful. My scenario is this: I have migrated a large web application from .NET Framework 4.7 to .NET 6 with the front end being developed with React. Many sections of this application query a list filtered by a "Status" property which is of type enum.
I know that if I could pass the string representation of the enum it would work but this requires editing a lot of components and I cannot do it.
I tried implementing
StringAsEnumResolver
, but that didn't change a thing. I have also tried to implement a CustomStringAsEnumResolver because I noticed that the methodPromoteBinaryOperandTypes()
doesn't care if one of the operands is an enum, but the debugger doesn't even hit the breakpoint I put in this class. How can we solve or workaround this? Submit new issue