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

NetCore: Composer / Component and RunTimeLevel Pattern #3054

Closed
KevinJump opened this issue Mar 8, 2021 · 10 comments
Closed

NetCore: Composer / Component and RunTimeLevel Pattern #3054

KevinJump opened this issue Mar 8, 2021 · 10 comments

Comments

@KevinJump
Copy link
Contributor

Discussion, Missing documentation, Umbraco NetCore Patterns

What article/section is this about?

https://our.umbraco.com/Documentation/Implementation/Composing/

Describe the issue

This is more of a question about best practices in Umbraco NetCore. @bergmania suggested this might be a good place to do this, as it can then feed into the documentation.

Composing in NetCore looks like it has changed and it would be good to know the best practice for patterns now.

In v8 you compose pretty much every time you want to do something at startup, listen to an event, etc.

So for example In v8:

namespace My.Website
{
    public class MyComposer: IUserComposer
    {
        public void Compose(Composition composition)
        {
            composition.Components().Append<MyComponent>();
        }
    }

    public class MyComponent: IComponent
    {
      private readonly MyCustomService _myService; 
       public void MyComponent(MyCustomService myService) 
       {
           _myService = myService;
       }
        // initialize: runs once when Umbraco starts
        public void Initialize()
        {
            myService.DoSomething();
        }

        // terminate: runs once when Umbraco stops
        public void Terminate()
        {
        }
    }
}

I suspect this has changed quite a bit in NetCore - because there is no longer a runtime check on composers - so they run all the time? and there appears to be a move towards NotificationHandlers for events (so you don't need a component?).

I might add a seperate question about NotificationHandlers, but i suspect that bit is still a work in progress? (i.e not all events have been moved to NotificationHandlers yet?

So in netCore ? is this the correct pattern if you want to do something at startup (when the site is running?) - or is there something i am missing ?

namespace DoStuff.Core
{
    public class MyCustomComposer : IUserComposer
    {
        public void Compose(IUmbracoBuilder builder)
        {
            // Add services
            builder.Services.AddUnique<MyCustomService>();

            // Add Notification Handlers
            builder.AddNotificationHandler<UmbracoApplicationStarting, MyApplicationStartingHandler>();
        }
    }

    public class MyApplicationStartingHandler : INotificationHandler<UmbracoApplicationStarting>
    {
        private readonly MyCustomService _myService;
        public MyApplicationStartingHandler(MyCustomService myService)
        {
            _myService = myService;
        }

        public void Handle(UmbracoApplicationStarting notification)
        {
            if (notification.RuntimeLevel >= Umbraco.Cms.Core.RuntimeLevel.Run)
            {
                _myService.DoSomething();
            }
        }
    }
}
@bergmania
Copy link
Member

I suspect this has changed quite a bit in NetCore - because there is no longer a runtime check on composers - so they run all the time? and there appears to be a move towards NotificationHandlers for events (so you don't need a component?).

Correct, we are moving towards NotificationHandlers, and thereby no static events.
If you only want to execute something for some runtime levels, you can inject IRuntimeState and use IRuntimeState.Level.

In your example, you would most likely only add the component using composition.Components().Append<MyComponent>(); if the runtime is Run.

So in netCore ? is this the correct pattern if you want to do something at startup (when the site is running?) - or is there something i am missing ?

Yep, that looks correct 💪

@KevinJump
Copy link
Contributor Author

KevinJump commented Mar 8, 2021

Ah OK cool.

So this is valid ?

public class MyComposer: IUserComposer
{
   private readonly IRuntimeState _runtimeState;
   public MyComposer(IRuntimeState runtimeState) {
         _runtimeState = runtimeState
   }

    public void Compose(Composition composition)
    {
        if (_runtimeState >= RuntimeLevel.Run) {
            composition.Components().Append<MyComponent>();
        }
    }
}

Which one would we say is 'preferred' ? (component vs NotificationHandler ?) - I know it might depend. but a couple of questions that might help people decide.

  • When do they fie (e.g are composers fired before UmbracoApplicationStarting)
  • Does the order get preserved ? (e.g if i use ComposeBefore/After on the composer will the order of the the notifications still be in the order they where registred) - might matter if you have for example migrations in one event and stuff using the db in another, so you would want to know they where in the right order.

@bergmania
Copy link
Member

When do they fie (e.g are composers fired before UmbracoApplicationStarting)

UmbracoApplicationStarting is fired as the last thing before components are initialized. Composers are called before the DI container is created.

Note that this event is fired before the first request.

Does the order get preserved ? (e.g if i use ComposeBefore/After on the composer will the order of the the notifications still be in the order they where registred) - might matter if you have for example migrations in one event and stuff using the db in another, so you would want to know they where in the right order.

They are executed in the same order as they are registered, yes.

@KevinJump
Copy link
Contributor Author

KevinJump commented Mar 8, 2021

So - to confirm

the order for things that normal users (e.g non-core code hackers) have it is :

  1. Composers
  2. UmbracoApplicationStarting
  3. Components

I am assuming it will not be recommended to change the boot sequence in program.cs or startup.cs ?

I don't know if its worth raising anything in the main repo but IUserComposer behavior has changed a bit based on this, in v8 it is only fired when RuntimeLevel = run - so people coding with IUserComposer currently don't have to worry about the site being up.

in NetCore this has changed to run always? So people use to that pattern will now get issues, because they will work when they develop them, but fail if the site is reinitialized or potentially during an upgrade. So its something that will need to be noted.

(i know there will be many breaking changes - but I would suggest this one will be asked in the forums a lot!)

update: meant IUserComposer not component

@bergmania
Copy link
Member

i know there will be many breaking changes - but I would suggest this one will be asked in the forums a lot!)

Good point. We could do that.. The thing is, now composers are components are more separated. In theory, we want all services to be available and therefore composed, no matter the runtime level. But the execution of the component should in this case only be done, if the runtime level is run. Not sure if there is a good way to archive that. or we should fall back to not composing IUserComposers, even that would mean some services are not available.

@KevinJump
Copy link
Contributor Author

I would say from a developer point of view - i think 99% of the time i am assuming that the code i have written is to run when Umbraco is setup and running - not before or during an upgrade.

Its more of an edge case to say i want code to run during setup or upgrades - and as they are not always things people will test with their code, they might get nasty surprises when 6 months later they update the site.

@bergmania
Copy link
Member

I agree in what you say, but do you see any problems if composers are executed, and thereby all services etc are available in the container, no matter the runtime level?
But then we should find a way to avoid executed components added in IUserComposers?

@KevinJump
Copy link
Contributor Author

KevinJump commented Mar 8, 2021

Yeah its more the component doing stuff and expecting dbs' ect to be there that is the issue - but i would be a little worried about the hidden nature of the logic that say,s all these services / config / notification things you just did are registered and executing but not the components line.

then again people may never notice because it would only be at install/upgrade time 🤷

maybe renaming IUserComposer with something else would be a better way to signal that this namespace doesn't do what it use to do?

anyway - might have gone of topic for the docs issue 😉

@bergmania
Copy link
Member

In core, we aim for not using Composers ourselves, so maybe we should just remove IUserComposer, so everyone needs to use the IComposer that still behaves the same

@umbrabot
Copy link

umbrabot commented Mar 4, 2022

Hiya @KevinJump,

Just wanted to let you know that we noticed that this issue got a bit stale and might not be relevant any more.

We will close this issue for now but we're happy to open it up again if you think it's still relevant (for example: it's a feature request that's not yet implemented, or it's a bug that's not yet been fixed).

To open it this issue up again, you can write @umbrabot still relevant in a new comment as the first line. It would be super helpful for us if on the next line you could let us know why you think it's still relevant.

For example:

@umbrabot still relevant
This bug can still be reproduced in version x.y.z

This will reopen the issue in the next few hours.

Thanks, from your friendly Umbraco GitHub bot 🤖 🙂

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

No branches or pull requests

4 participants