Skip to content

issue #568: Error when function/action name contains keyword "On" - #576

Merged
xuzhg merged 4 commits into
mainfrom
issue568
Apr 27, 2022
Merged

issue #568: Error when function/action name contains keyword "On"#576
xuzhg merged 4 commits into
mainfrom
issue568

Conversation

@xuzhg

@xuzhg xuzhg commented Apr 22, 2022

Copy link
Copy Markdown
Member

issue #568

Describe the bug
when function/action name contains keyword "On", the operation convention can't correct parse the action name.
/odata/SEChat/SE.GetStatusOnLineOfflineUser => error
/odata/SEChat/SE.StatusLineOfflineUserOn => error

What is in the PR:

  1. Search the operation using the whole action name first, if found, use it.
  2. Split the action name, and search again.

isOnCollection = false;
string operation;
int index = actionName.IndexOf("OnCollectionOf", StringComparison.Ordinal);
int index = actionName.LastIndexOf("OnCollectionOf", StringComparison.Ordinal);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do these comparisons also need to be updated to honor context.Options?.RouteOptions?.EnableActionNameCaseInsensitive?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#Resolved.

castTypeFromActionName = entityType.FindTypeInInheritance(context.Model, cast, context.Options?.RouteOptions?.EnableActionNameCaseInsensitive == true) as IEdmEntityType;
if (castTypeFromActionName == null)
castTypeFromActionName = null;
if (cast != null)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do I understand correctly that these early returns are an optimization and not actually involved with addressing the bug? In other words, wouldn't it make sense to factor the code this way:

string operationName = SplitActionName(actionName, out string cast, out isOnCollection);
castTypeFromActionName = null;
if (cast != null)
{
  if (cast.Length == 0)
  {
    return;
  }

  castTypeFromActionName = entityType.FindTypeInInheritance(context.Model, cast, context.Options?.RouteOptions?.EnableActionNameCaseInsensitive == true) as IEdmEntityType;
  if (castTypeFromActionName == null)
  {
    return;
  }
}

IEdmOperation[] candidates = FindCandidates(context, actionName);
if (candidates.Length == 0)
{
  candidates = FindCandidates(context, operationName);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do I understand correctly that these early returns are an optimization and not actually involved with addressing the bug?

If that's really the case, it would be nice to avoid making this change in the same PR IMHO. Or at the very least, the change should be done in a separate commit in the branch.

// If we have multiple same function defined, we should match the best one?

StringComparison actionNameComparison = context.Options?.RouteOptions?.EnableActionNameCaseInsensitive == true ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
return context.Model.SchemaElements.OfType<IEdmOperation>().Where(f => f.IsBound && f.Name.Equals(operationName, actionNameComparison)).ToArray();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why .ToArray()? The code was already running without it, and I don't see how adding it improves anything

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we will do use it:

  1. check the count
  2. iterator
    So, it could be an improvement to call toArray().

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#Resolved.

Comment on lines +71 to +72
// Let's find the operations using the action first,
// If not founding, let's split the action name, search again

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These comments imply to me we have 2 strategies, and a fallback/priotity between them.

It would be nice to model these as 2 distinct functions and then have an explicit fallback using ?? or even a small chain of responsibility implementation (though that might be a bit overkill here).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactor codes, please take a look. Thanks.

// If we have multiple same function defined, we should match the best one?

StringComparison actionNameComparison = context.Options?.RouteOptions?.EnableActionNameCaseInsensitive == true ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
return context.Model.SchemaElements.OfType<IEdmOperation>().Where(f => f.IsBound && f.Name.Equals(operationName, actionNameComparison)).ToArray();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Long lines like this are really hard to read.

I'd suggest reformatting like:

Suggested change
return context.Model.SchemaElements.OfType<IEdmOperation>().Where(f => f.IsBound && f.Name.Equals(operationName, actionNameComparison)).ToArray();
return context.Model.SchemaElements
.OfType<IEdmOperation>()
.Where(f => f.IsBound && f.Name.Equals(operationName, actionNameComparison))
.ToArray();

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#Resolved.

{
return;
}
if (candidates == null || candidates.Length == 0)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make it so that none of the FindCandidates overloads ever return null, and instead make it return an empty array (for instance, using Array.Empty<T>)?

Handling null collections like this is usually ill-advised unless there is a very clear semantic value difference between null and empty (which I don't think is the case here).

We could then simplify this back to:

Suggested change
if (candidates == null || candidates.Length == 0)
if (candidates.Length == 0)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#Resolved.

corranrogue9
corranrogue9 previously approved these changes Apr 27, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants