简体   繁体   中英

Registering factory method doesn't prevent Autofac from calling ctor

I have a service called ServiceConfiguration wich has a non-default ctor. I'm trying to register it through Autofac with a factory method:

builder.Register(c=>LoadConfiguration())
       .As<ServiceConfiguration>();

And here is the simple LoadConfiguration method:

private ServiceConfiguration LoadConfiguration() {
    const string fileName = "app.json";
    var json = File.ReadAllText(fileName, Encoding.UTF8);
    var model = JsonConvert.DeserializeObject<ServiceConfiguration>(json);
    return model;
}

I expect Autofac to call the LoadConfiguration when I asked to resolve ServiceConfiguration . But it seems it's trying to call a ctor on ServiceConfiguration . Eg calling this line:

var c = container.Resolve<ServiceConfiguration>();

causes this error:

Autofac.Core.DependencyResolutionException: 'An error occurred during the activation of a particular registration. See the inner exception for details. Registration: Activator = ServiceConfiguration (ReflectionActivator), Services = [ServiceConfiguration], Lifetime = Autofac.Core.Lifetime.CurrentScopeLifetime, Sharing = Shared, Ownership = OwnedByLifetimeScope'

and the InnerException is:

DependencyResolutionException: None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'ServiceConfiguration' can be invoked with the available services and parameters: Cannot resolve parameter 'JobsConfiguration jobs' of constructor 'Void .ctor(JobsConfiguration)'.

Can you find out what I'm missing?

UPDATE: I'm not missing a registration of JobsConfiguration . Actually it's not a registrable type and I don't want to register it. I don't want to ctor get called at all (the service is getting build from a json file), instead whenever somebody asked for ServiceConfiguration I want Autofac to call LoadConfiguration() and use it's returned value.

UPDATE2:

After a while, it seems extracting an interface from ServiceConfiguration - say IServiceConfiguration - and registering/resolving the interface works just fine. But i cannot figure out why! I mean this:

builder.Register(c=>LoadConfiguration())
       .As<IServiceConfiguration>();

var c = container.Resolve<IServiceConfiguration>();

works. But the concrete version not. So why? What's the difference?

You should strive separating the loading of configuration files from Object Composition. This means that it's best not to load the configuration when Resolve is called. Changing this is trivial:

ServiceConfiguration configuration = LoadConfiguration();
builder.RegisterInstance(configuration);

That said, your given registration should work:

builder.Register(c => LoadConfiguration())
    .As<ServiceConfiguration>();

Which is identical to:

builder.Register(c => LoadConfiguration());

With this registration, Autofac will not Auto-Wire the ServiceConfiguration constructor, but will leave that to the supplied delegate. There might be something going on that your post is not showing.

I used the following MCVE , using Autofav 4.8.1, to verify that this works:

class ServiceConfiguration
{
    public ServiceConfiguration(int a, double b) => Console.WriteLine("ctor called.");
}

static ServiceConfiguration LoadConfiguration() => new ServiceConfiguration(2, 6);

static void Main(string[] args)
{
    var builder = new ContainerBuilder();

    builder.Register(_ => LoadConfiguration()).As<ServiceConfiguration>();

    builder.Build().Resolve<ServiceConfiguration>();
}

Last note: Make sure that ServiceConfiguration is not an application-wide configuration class that is injected into many consumers. To understand what's wrong with application-wide configuration classes, please read this Q/A .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM