Skip to content

Allow injecting additional registrations during the creation of service lifetimescope #57

@n3gwave

Description

@n3gwave

Problem Statement

Service registration is closed for extension and makes injecting additional dependencies within the SF service lifetime scope impossible. There is a challenge with proper initializing the Application Insights with Autofac. Based on AI for SF docu (https://github.com/microsoft/ApplicationInsights-ServiceFabric) there is a requirement to register FabricTelemetryInitializerExtension.CreateFabricTelemetryInitializer(ServiceContext) as a ITelemetryInitializer. However, this must be done while creating the lifetimescope in RegisterServiceAsync method. Otherwise, we don't have ServiceContext available.

Desired Solution

This can be easily achieved by adding additional parameter of type Action to RegisterStatefulServiceFactory/RegisterStatelessServiceFactory/RegisterActorFactory methods and make sure the parameter is passed from AutofacServiceExtensions class methods. Then in factory methods we can add custom dependencies in Begin scope action

var lifetimeScope = container.BeginLifetimeScope(tag, builder =>
                {
                    builder.RegisterInstance(context)
                        .As<StatefulServiceContext>()
                        .As<ServiceContext>();

                    customRegistrations?.Invoke(builder);
                });

Then we could register service with following code:

                Action<ContainerBuilder> configure = b =>
                {
                    AppInsightsSupportInjectAction(b);

                    b.RegisterInstance(new StateManagerHolder());
                    b.Register(ctx => ctx.Resolve<StateManagerHolder>().StateManager ?? new ReliableStateManager(resolve(ctx)))
                        .As<IReliableStateManager>().InstancePerDependency();
                };

                try
                {
                    containerBuilder.RegisterStatefulService<TService>(serviceTypeName, serviceTypeName, configure => configure.Register(ctx => FabricTelemetryInitializerExtension.CreateFabricTelemetryInitializer(ctx.Resolve<ServiceContext>()))

Alternatives You've Considered

I haven't found any workaround to initialize the SF telemetry initializer in the scope created for SF service. I cannot do it in the Kestrel Webhost, as I have remoting methods (my service implements the SF's IService interface).

Additional Context

In my solution, I use a mix of HostBulder with Autofac along with Autofac SF integration so
registering FabricTelemetryInitializer out of service scope ends with exception as serviceContext is not resolved during the Build operation on HostBuilder. The reason for that is the AI integration which needs to resolve ILogger thus it pulls for all ITelemetryInitializers as well.

Additional benefit is that we could also register the IReliableStateManager. Now it's not possible because state manager is properly created withing the StatefulService and lifetime scope registration is being done befor. However with some helper class:

public class StateManagerHolder
    {
        /// <summary>
        /// Gets or sets the state manager.
        /// </summary>
        public IReliableStateManager StateManager { get; set; }
    }

we can inject additional registration

Action<ContainerBuilder> configure = b =>
                {
                    b.RegisterInstance(new StateManagerHolder());
                    b.Register(ctx => ctx.Resolve<StateManagerHolder>().StateManager ?? new ReliableStateManager(resolve(ctx)))
                        .As<IReliableStateManager>().InstancePerDependency();
                };

and make sure we update reference StateManager in RunAsync as follows:

            var holder = this.rootScope.Resolve<StateManagerHolder>();
            if (holder.StateManager == null)
            {
                holder.StateManager = this.StateManager;
            }

the last step is required as for some reason new ReliableStateManager(resolve(ctx)) doesn't create valid reliable manager object - at least in my case injected stateful context in ctor is not assigned properly.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions