Search code examples
vb.netasp.net-coredependency-injection

How do I inject services into a factory function (ModelBuilderFactory) in ASP.NET Core 7


I'm struggling to access a service when configuring a factory function in our app setup.

We're using Asp.Versioning, and I'm trying to inject a service for use in our custom model builder.

Here is the relevant configuration section

builder.Services.AddSingleton(Of EdmTypeReplacer)
builder.Services.AddApiVersioning() _
    .AddMvc() _
    .AddOData(Sub(o)
        o.ModelBuilder.OnModelCreated = Sub(modelBuilder, model)
                ' I want to use my injected EdmTypeReplacer here.
                ' Not a static class instance (which I'm using currently)
                Dim replacer = EdmTypeReplacer.Instance
                replacer.OnModelCreated(model)
            End Sub
        o.ModelBuilder.ModelBuilderFactory = Function()
                ' I want to use my injected EdmTypeReplacer here.
                ' Not a static class instance (which I'm using currently)
                Dim replacer = EdmTypeReplacer.Instance
                Return New CustomConventionODataModelBuilder(replacer)
            End Function
        End Sub) _
    .AddODataApiExplorer()

I'm trying to inject a singleton EdmTypeReplacer into CustomConventionODataModelBuilder, but I can't pass a service to the ModelBuilderFactory Function params because it is expecting an empty function delegate.

I need access to the same instance of EdmTypeReplacer in the delegate for OnModelCreated too.

I'm not sure how/whether I can access the ServiceProvider as it's still being built.

I've currently been forced to create EdmTypeReplacer as a static class singleton instance and use that, but I'm convinced there must be a way to inject this, or at least access the ServiceProvider in the factory function.

Feel free to give any answers in C# instead of VB, if you're more familiar with that.

*** Additional info ***

CustomConventionODataModelBuilder configures EdmTypeReplacer with changes which are used later on when OnModelCreated is called. So, it is necessary for both to use the same instance (ideally a singleton instance, as reflection is used in this process).


Solution

  • @Brando Zhang's answer got me on the right track.

    We can capture an instance of EdmTypeReplacer and then pass this instance directly to the OnModelCreated and ModelBuilderFactory functions. The same instance is then returned in the AddSingleton factory method so all classes get the same instance, instead of a duplicate instance when IServiceProvider is actually built.

    This avoids us needing to create a partially completed service provider using builder.Services.BuildServiceProvider() as we already have a reference to the instance we need in the same scope.

    Dim replacer = new EdmTypeReplacer
    builder.Services.AddSingleton(Of EdmTypeReplacer)(Function(s) replacer)
    builder.Services.AddApiVersioning() _
        .AddMvc() _
        .AddOData(Sub(o)
            o.ModelBuilder.OnModelCreated = Sub(modelBuilder, model)
                    replacer.OnModelCreated(model)
                End Sub
            o.ModelBuilder.ModelBuilderFactory = Function()
                    Return New CustomConventionODataModelBuilder(replacer)
                End Function
            End Sub) _
        .AddODataApiExplorer()