Thanks you @Stinky Buffalo for the answer.
i change your code and resolve error when add duplicate key in dictionary.
example:
CreateDate%20gt%202021-05-22T00:00:00Z%20and%20CreateDate%20lt%202021-05-26T00:00:00Z%20
and also:
BookRequestType%20eq%20%27BusDomestic%27%20or%20BookRequestType%20eq%20%27TrainDomestic%27%20or%20BookRequestType%20eq%20%27FlightDomestic%27%20
The following code worked very well for me:
first install Install-Package Microsoft.Data.OData -Version 5.8.4
package.
then create class with name 'ODataHelper' and after that copy below codes:
public class ODataHelper<T> where T : class
{
private static readonly TextInfo TextInfo = new CultureInfo("en-US", false).TextInfo;
public static Dictionary<string, Tuple<object, ODataOperatorType>> ODataUriParser(
ODataQueryOptions<T> queryOptions)
{
var dictFilters = new Dictionary<string, Tuple<object, ODataOperatorType>>();
TryNodeValue(queryOptions.Filter?.FilterClause?.Expression, dictFilters);
return dictFilters;
}
private static void TryNodeValue(SingleValueNode node,
IDictionary<string, Tuple<object, ODataOperatorType>> dictFilters)
{
if (node is null)
return;
if (node is SingleValueFunctionCallNode valueFunction)
{
ParseSingleFunctionNode(valueFunction,
Enum.Parse<ODataOperatorType>(TextInfo.ToTitleCase(valueFunction.Name)), dictFilters);
}
if (node is BinaryOperatorNode binaryOperatorNode)
{
var left = binaryOperatorNode.Left;
var right = binaryOperatorNode.Right;
if (left is SingleValuePropertyAccessNode leftNodeRight && right is ConstantNode rightNodeRight)
{
ParseSingleValueNode(
leftNodeRight,
rightNodeRight,
Enum.Parse<ODataOperatorType>(binaryOperatorNode.OperatorKind.ToString()),
dictFilters);
}
switch (left)
{
case ConvertNode node1:
{
var convertLeft = node1.Source;
if (convertLeft is SingleValuePropertyAccessNode leftNodeLeft &&
right is ConstantNode rightNodeLeft)
{
ParseSingleValueNode(
leftNodeLeft,
rightNodeLeft,
Enum.Parse<ODataOperatorType>(
binaryOperatorNode.OperatorKind.ToString()),
dictFilters);
}
else
TryNodeValue(node1.Source, dictFilters);
break;
}
case BinaryOperatorNode:
TryNodeValue(left, dictFilters);
break;
case SingleValueFunctionCallNode functionNode:
ParseSingleFunctionNode(functionNode,
Enum.Parse<ODataOperatorType>(TextInfo.ToTitleCase(functionNode.Name)),
dictFilters);
break;
}
switch (right)
{
case BinaryOperatorNode:
TryNodeValue(right, dictFilters);
break;
case ConvertNode convertNode:
TryNodeValue(convertNode.Source, dictFilters);
break;
case SingleValueFunctionCallNode functionNode:
ParseSingleFunctionNode(functionNode,
Enum.Parse<ODataOperatorType>(TextInfo.ToTitleCase(functionNode.Name)),
dictFilters);
break;
}
}
}
private static void ParseSingleValueNode(
SingleValuePropertyAccessNode left,
SingleValueNode right,
ODataOperatorType operatorKind,
IDictionary<string, Tuple<object, ODataOperatorType>> dictFilters)
{
string key = left.Property.Name.Trim();
object value = ((ConstantNode) right).Value;
object specifiedValue = value is ODataEnumValue enumValue ? enumValue.Value : value;
if (operatorKind is ODataOperatorType.LessThan or ODataOperatorType.LessThanOrEqual)
{
dictFilters.TryAdd($"{key}_To", new Tuple<object, ODataOperatorType>(value, operatorKind));
}
else if (dictFilters.TryGetValue(key, out Tuple<object, ODataOperatorType> currentValue))
{
dictFilters[key] = new Tuple<object, ODataOperatorType>(
$"{currentValue.Item1},{specifiedValue}",
operatorKind);
}
else
{
dictFilters.Add(key, new Tuple<object, ODataOperatorType>(specifiedValue, operatorKind));
}
}
private static void ParseSingleFunctionNode(
SingleValueFunctionCallNode node,
ODataOperatorType operatorKind,
IDictionary<string, Tuple<object, ODataOperatorType>> dictFilters)
{
string key = (node.Parameters.First() as SingleValuePropertyAccessNode)?.Property.Name.Trim();
object value = (node.Parameters.Last() as ConstantNode)?.Value;
if (string.IsNullOrEmpty(Convert.ToString(value)?.Trim()))
return;
dictFilters.TryAdd(key, new Tuple<object, ODataOperatorType>(value, operatorKind));
}
}
public enum ODataOperatorType
{
Equal,
NotEqual,
GreaterThan,
GreaterThanOrEqual,
LessThan,
LessThanOrEqual,
Contains
}
for call ODataUriParser method you need to get the value from the input action.
get ODataQueryOptions<YourObjectModel> from request api :
input endpoint action => ODataQueryOptions<YourObjectModel> options
public Task<IQueryable<YourObject>> Get(ODataQueryOptions<YourObject> options)
{
// call your service class
}
then write below codes in your service class for call ODataUriParser and use the result runction:
Dictionary<string, Tuple<object, ODataOperatorType>> dictFilters =
ODataHelper<YourObject>.ODataUriParser(options);
An example of how to use ODataUriParser method result:
if (dictFilters.TryGetValue("Email", out Tuple<object, ODataOperatorType> emailValue))
{
bookRequestProfileDto.Email =
Convert.ToDateTime(emailValue.Item1.ToString());
}
For example, we want to convert a list of numeric strings to a list of text strings using the Enum:
BookRequestType is enumuration class.
if (dictFilters.TryGetValue("BookRequestType", out Tuple<object, ODataOperatorType> bookRequestTypeValue))
{
customerTransactionDto.BookRequestType =
Convert.ToString(bookRequestTypeValue.Item1)
.ConvertStringNamesEnumToStringNumbers<BookRequestType>();
}
// Extesion Method
public static string ConvertStringNamesEnumToStringNumbers<T>(this string stringTypes) where T : Enum
{
var separateStringTypes = stringTypes.Split(',');
StringBuilder stringBuilder = new StringBuilder();
foreach (var item in separateStringTypes)
{
stringBuilder.Append((int) Enum.Parse(typeof(T), item)).Append(',');
}
return stringBuilder.ToString().Remove(stringBuilder.Length - 1);
}