Skip to content
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

Register DisplayDrivers with ContentOptions #4361

Merged
merged 37 commits into from
Feb 7, 2020

Conversation

deanmarcussen
Copy link
Member

@deanmarcussen deanmarcussen commented Sep 22, 2019

Far too late to be wanting to get this in now, but further to our conversation @sebastienros here's some ideas for registering DisplayDriver Types in the new ContentOptions, with a factory to resolve them from DI as required, rather than resolving all of them at once and iterating.

Just ideas for the future, but if you like the concept, then this would work for Handlers, as well.

Did think about using a dictionary of Func<> but the factory seemed better

Also will probably need to be changed to a Lookup to support multiple driver registrations against the same part, eg CommonPart Drivers

@deanmarcussen
Copy link
Member Author

@jtkech Thanks for taking a look last night.

I had some time this morning so I refactored, based on your suggestions, and some other things that I wanted to try.

  • Removed the lookup dictionary on ContentOptions, and implemented ContentDisplayOptions as a complement too it. Felt it was a bit too leaky, and as this is all about perf, better to have an actual property to use.
  • Renamed factories to Resolvers. Much better convention
  • Removed weird generic type checking. Better to just rely on where T : IContentPartDisplayDriver
  • Made it support multiple drivers and handlers for the same part, including registering seperately, if for example, they are part of a feature.
  • Implemented for all Fields, and Handlers as well, to see if it benchmarks any better.

I guess this PR is all about performance, so if @sebastienros feels like benchmarking it, that would be the only way to see if what I've done is even remotely useful?

@carlwoodhouse
Copy link
Member

if nothing else its much more intuitive :)

@deanmarcussen
Copy link
Member Author

@sebastienros Any feedback on this idea?

@deanmarcussen deanmarcussen changed the title WIP. Some ideas for registering types with ContentOptions Register DisplayDrivers with ContentOptions Oct 25, 2019
@deanmarcussen
Copy link
Member Author

@jtkech I went through this again to add some logging, and am happy with how it looks, if you had any thoughts they'd be appreciated

@jtkech
Copy link
Member

jtkech commented Oct 26, 2019

@deanmarcussen okay no problem, i will look at it asap, but so many things ;)

# Conflicts:
#	src/OrchardCore.Modules/OrchardCore.Liquid/Startup.cs
#	src/OrchardCore/OrchardCore.ContentManagement.Display/ContentDisplay/ContentItemDisplayCoordinator.cs
#	src/OrchardCore/OrchardCore.ContentManagement/Handlers/ContentPartHandlerCoordinator.cs
{
InvokeExtensions.HandleException(ex, Logger, displayDriver.GetType().Name, "BuildDisplayAsync");
try
Copy link
Member Author

Choose a reason for hiding this comment

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

@sebastienros on your recent pr #4887 you've reduced the allocation pattern for using the invoke extensions.
Just noting that the current BuildDisplayAsync method doesn't use the
await partDisplayDrivers.InvokeAsync(async (driver, part, typePartDefinition, context) => technique at all, it is only the BuildEditorAsync that used it previously.
So if there is an opportunity to reduce allocations on BuildDisplayAsync (regardless of whether this PR goes through), perhaps we should use them?

@deanmarcussen deanmarcussen mentioned this pull request Nov 28, 2019
@deanmarcussen
Copy link
Member Author

deanmarcussen commented Dec 30, 2019

ok @sebastienros I reimplemented this with explicit registrations for display modes and editors.

By default registration is * to indicate all display modes.

Other options include registering as

services.AddContentField<MarkdownField>()
    .WithDisplayDriver<MarkdownFieldDisplayDriver>(o =>
    {
        o.ForDisplayModes("*"); // use this driver for all display modes
        o.ForDisplayModes("standard", "my-custom-display-mode"); // use this driver for standard and custom
        o.ForDisplayModes("my-custom-display-mode"); // just my custom mode
});

I have left them as mutable HashSets so registration can be mutated by a user, being able to alter the default registrations if they are implementing a custom editor and want to change the default registrations.

Note parts only have custom editors , not display modes.

Edited

@deanmarcussen
Copy link
Member Author

Updated this after our conversation Seb

Here are some examples of how registration could be done for fields

// Taxonomy Field - Standard registration, will automatically add a Func return true to editor and displaymode
services.AddContentField<TaxonomyField>()
  .UseDisplayDriver<TaxonomyFieldDisplayDriver>();

// Alter display mode in third party module. Run normal driver for standard mode. Run Tag driver for Tags
services.AddContentField<TaxonomyField>() //can be called multiple times to access builder again
  .ForDisplayMode<TaxonomyFieldDisplayDriver>(d => String.IsNullOrEmpty(d) ? true : false)
  .ForDisplayMode<TagFieldDisplayDriver>(d => String.Equals(d, "Tags", StringComparison.OrdinalIgnoreCase) ? true : false);

// Alter editor in third party module
services.AddContentField<TaxonomyField>()
  .ForEditor<TaxonomyFieldDisplayDriver>(e => String.IsNullOrEmpty(e) ? true : false)
  .ForEditor<TagFieldDisplayDriver>(e => String.Equals(e, "Tags", StringComparison.OrdinalIgnoreCase) ? true : false);

// Alter both display mode and editor in third party module
services.AddContentField<TaxonomyField>()
  .UseDisplayDriver<TaxonomyFieldDisplayDriver>(f => String.IsNullOrEmpty(f) ? true : false)
  .UseDisplayDriver<TagFieldDisplayDriver>(d => String.Equals(d, "Tags", StringComparison.OrdinalIgnoreCase) ? true : false);

// Totally remove the default driver (saves running a func against it), and use a custom driver.
services.AddContentField<TaxonomyField>()
  .RemoveDisplayDriver<TaxonomyFieldDisplayDriver>()
  .UseDisplayDriver<MyCustomFieldDisplayDriver>();

Handlers I moved to .Add and .Remove

// Taxonomy Part
  services.AddContentPart<TaxonomyPart>()
    .UseDisplayDriver<TaxonomyPartDisplayDriver>()
    .AddHandler<TaxonomyHandler>();

// Remove handler in third party module and replace with a new handler
services.AddContentPart<TaxonomyPart>()
  .UseDisplayDriver<TaxonomyPartDisplayDriver>()
  .RemoveHandler<TaxonomyHandler>()
  .AddHandler<CustomTaxonomyHandler>();  

So let me know if you agree with the design and like the naming conventions (easy to change)

@deanmarcussen deanmarcussen merged commit 431f102 into dev Feb 7, 2020
@deanmarcussen deanmarcussen added this to the 1.0 milestone Feb 7, 2020
@sebastienros sebastienros deleted the deanmarcussen/display-factories branch February 7, 2020 23:13
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.

4 participants