-
Notifications
You must be signed in to change notification settings - Fork 2.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix case insensitive dictionary problem (Lombiq Technologies: OCORE-197) #16623
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sarahelsaig what line fixed the referenced issue?
@@ -352,13 +352,17 @@ public async Task<ActionResult> ListPost(ContentOptions options, IEnumerable<str | |||
{ | |||
return Forbid(); | |||
} | |||
|
|||
// Prevent multiple enumeration. | |||
var itemIdList = itemIds as IList<string> ?? itemIds?.ToList() ?? new List<string>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure this is needed. Can you explain how this prevents multiple enumerations?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here you had itemIds.Contains(x.Key)
. This means itemIds
was enumerated templatesDocument.Templates.Count
times, which is wasteful if the object is not already IList<string>
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about this instead?
The following code is more readable, does reduce enumeration, does not use as much memory and does not waste resources when Remove
is not being request.
if (itemIds?.Any() == true)
{
switch (options.BulkAction)
{
case ContentsBulkAction.None:
break;
case ContentsBulkAction.Remove:
var templatesDocument = options.AdminTemplates
? await _adminTemplatesManager.LoadTemplatesDocumentAsync()
: await _templatesManager.LoadTemplatesDocumentAsync();
foreach (var itemId in itemIds)
{
if (!templatesDocument.Templates.ContainsKey(itemId))
{
continue;
}
if (options.AdminTemplates)
{
await _adminTemplatesManager.RemoveTemplateAsync(itemId);
continue;
}
await _templatesManager.RemoveTemplateAsync(itemId);
}
await _notifier.SuccessAsync(H["Templates successfully removed."]);
break;
default:
return BadRequest();
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The if (itemIds?.Any() == true)
also does as an enumeration on its own. I guess it can be replaced with if (itemIds != null)
without too much performance hit (probably even a perf gain on average as it should be very rare that an empty ID list is posted.
Also it occurred to me that this code compares keys. If we use emplatesDocument.Templates.Keys
, then we are just looking for the union of two string sets. Linq already has an extension method for that. see commit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes .Any()
iterates over the IEnumerable checking if there is at least one item. This is okay because it's fast since it does not iterate over every item. May be better to use itemIds != null && itemIds.Any()
this way you don't bother loading the document or applying the union logic where there is no work to be done.
The changes in the file src/OrchardCore.Modules/OrchardCore.Templates/Models/TemplatesDocument.cs fixed the issue. I have made the same changes for all other dictionary properties with a |
Instead of hard-coding this into the documents, shouldn't we use a |
That would be a converter that checks every object for dictionary properties with a particular default value. So some kind of a partial converter. Is that actually possible in STJ? |
IIRC, you can apply a converter to a property, via the |
Ok, I did that. I was confused, because this still involves some code you have to add to each dictionary prop. |
|
||
namespace OrchardCore.BackgroundTasks.Models; | ||
|
||
public class BackgroundTaskDocument : Document | ||
{ | ||
[JsonConverter(typeof(CaseInsensitiveDictionaryConverter<BackgroundTaskSettings>))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You see how these properties already had a dictionary with the correct casing? I believed that's a setting on the JSON options to prefer using the existing instance instead of creating a new one (which is what the attribute does).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MikeAlhayek Nice find. Unfortunately it's only for Dictionary<string, string>
instead of Dictionary<string, TValue>
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sarahelsaig do you think https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializeroptions.preferredobjectcreationhandling?view=net-8.0#system-text-json-jsonserializeroptions-preferredobjectcreationhandling would be the setting to set to get parity with the previous behavior? Which is a perfectly fine default IMO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's already set in JOptions.Base
. So looks like it's not enough by itself.
@sarahelsaig Please remove |
Ok, removed. |
src/OrchardCore.Modules/OrchardCore.Templates/Controllers/TemplateController.cs
Outdated
Show resolved
Hide resolved
public Processing.ResizeMode Mode { get; set; } | ||
public Processing.Format Format { get; set; } | ||
public ResizeMode Mode { get; set; } | ||
public Format Format { get; set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I called Rider's "Organize imports" feature to get rid of dead using
namespaces and it did this. I think it looks better, but comment here if I should revert them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, but let's see what @sebastienros and @MikeAlhayek have to say.
Fixes #16585
Easiest way to test: