Search code examples
c#genericsdependency-injectionautofacfactory-pattern

Access generic type of requested service for parameter construction


I've got a service contract for a strongly-typed configuration:

public interface IConfigurationSource<TConfiguration> {
    TConfiguration Current { get; }
}

and an implementation based on YAML files:

public class YamlFileConfigurationSource<TConfiguration> 
    : IConfigurationSource<TConfiguration> {

    public YamlFileConfigurationSource(string fileName) { }

    ...
}

Now I'm trying to register the implementation within Autofac in a way that the fileName parameter can be constructed based on the generic type of the requested service. So when a client requests YamlFileConfigurationSource<MyCustomConfigurationModel> a path like MyCustomConfigurationModel.config can be provided.

I already tried with ContainerBuilder.RegisterGeneric(), .WithConstructor() and delegate factories, however I'm somehow unable to see how to access the generic type during registration.


Solution

  • YamlFileConfigurationSource should not depends on fileName you can get it using typeof(TConfiguration)

    public class YamlFileConfigurationSource<TConfiguration>
        : IConfigurationSource<TConfiguration> {
    
        public YamlFileConfigurationSource() { }
    
        public TConfiguration Current { 
            get {
                String fileName = typeof(TConfiguration).Name + ".config";
                // get config from fileName
            }
        }
    }
    

    If you want to dissociate the code where TConfiguration is converted to a fileName you can introduce a new component.

    public interface IConfigurationFileProvider<TConfiguration> {
        String GetFileName(); 
    }
    public class SimpleConfigurationFileProvider<TConfiguration>
        : IConfigurationFileProvider<TConfiguration>  {
        public String GetFileName() {
            return typeof(TConfiguration) + ".config";
        }
    }
    

    and add this dependency on the constructor of YamlFileConfigurationSource

    The registration will look like this :

    builder.RegisterGeneric(typeof(YamlFileConfigurationSource<>))
            .As(typeof(IConfigurationSource<>));
    builder.RegisterGeneric(typeof(SimpleConfigurationFileProvider<>))
            .As(typeof(IConfigurationFileProvider<>));
    

    By the way, for educational purpose, this is the way to do using the WithParameter method

    builder.RegisterGeneric(typeof(YamlFileConfigurationSource<>))
            .As(typeof(IConfigurationSource<>))
            .WithParameter((pi, c) => pi.Name == "fileName",
                           (pi, c) => pi.Member.DeclaringType.GetGenericArguments()[0].Name);
    

    It should work, but it's a workaround, whereas the previous solution was more elegant.