Passing parameters to constructors using Autofac

前端 未结 4 1463
说谎
说谎 2021-01-31 02:42

I\'m very new to autofac so it\'s possible that I\'m completely misusing it.

Let\'s say I have a class that has this structure:

public class HelperClass          


        
相关标签:
4条回答
  • 2021-01-31 02:50

    Autofac does not use non-public constructors. By default, it only finds public ones and simply doesn't see the others. Unless you use .FindConstructorsWith(BindingFlags.NonPublic), it will see only public constructors. Therefore your scenario should work as you expect it to do.

    0 讨论(0)
  • 2021-01-31 02:51

    You can always use the WithParameter method to explicitly specify a constructor parameter:

    builder.RegisterType<DoesSomething>()
           .As<IDoesSomething>()
           .WithParameter("helper", new HelperClass("do", "something"));
    
    builder.RegisterType<DoesSomethingElse>()
           .As<IDoesSomethingElse>()
           .WithParameter("helper", new HelperClass("do", "somethingelse"));
    

    As far as I can tell there is no need for an interface for HelperClass because it essentially is just a value holder.

    For this to work you would need to make the internal constructor public, I think.

    0 讨论(0)
  • 2021-01-31 02:54

    There are two ways to pass parameters in Autofac:

    When you are registering the component:

    When you register components, you have the ability to provide a set of parameters that can be used during the resolution of services based on that component. Autofac offers several different parameter matching strategies:

    • NamedParameter - match target parameters by name
    • TypedParameter - match target parameters by type (exact type match required)
    • ResolvedParameter - flexible parameter matching

      // Using a NAMED parameter:
      builder.RegisterType<ConfigReader>()
         .As<IConfigReader>()
         .WithParameter("configSectionName", "sectionName");// parameter name, parameter value. It's the same of this: new NamedParameter("configSectionName", "sectionName")
      
      // Using a TYPED parameter:
      builder.RegisterType<ConfigReader>()
         .As<IConfigReader>()
         .WithParameter(new TypedParameter(typeof(string), "sectionName"));
      
      // Using a RESOLVED parameter:
      builder.RegisterType<ConfigReader>()
         .As<IConfigReader>()
         .WithParameter(
           new ResolvedParameter(
             (pi, ctx) => pi.ParameterType == typeof(string) && pi.Name == "configSectionName",
             (pi, ctx) => "sectionName"));
      

      NamedParameter and TypedParameter can supply constant values only.

      ResolvedParameter can be used as a way to supply values dynamically retrieved from the container, e.g. by resolving a service by name.

    In case you want to pass as parameter a service that is already registered, eg, IConfiguration, you can resolve the parameter as I show below:

        builder.RegisterType<Service>()
               .As<Iervice>()
               .WithParameter((pi, ctx) => pi.ParameterType == typeof(IConfiguration) && pi.Name == "configuration",
                              (pi, ctx) => ctx.Resolve<IConfiguration>());
    

    When you are resolving the component:

    One way to pass parameter at runtime in Autofac is using the Resolve method. You could create a class like this:

    public class ContainerManager
    {
      public IContainer Container {get;set;}
      //...
      public T[] ResolveAllWithParameters<T>(IEnumerable<Parameter> parameters)
      {
        return Container.Resolve<IEnumerable<T>>(parameters).ToArray();
      }
    }
    

    Parameter is an abstract class that belongs to Autofac, you can use the NamedParameter class to pass the parameters that you need. You can use the ContainerManager class as I show below:

        public T[] ResolveAllWithParameters<T>(IDictionary<string,object> parameters )
        {
            var _parameters=new List<Parameter>();
            foreach (var parameter in parameters)
            {
                _parameters.Add( new NamedParameter(parameter.Key, parameter.Value));
            }
            return ContainerManager.ResolveAllWithParameters<T>(_parameters);
        }
    

    This way you can pass the parameters at runtime using a Dictionary<string, object> when you are resolving an specific component.

    Using an Extension Method could be even more simple:

    public static class ContainerExtensions
    {
        public static T[] ResolveAllWithParameters<T>(this IContainer Container, IDictionary<string, object> parameters)
        {
            var _parameters = new List<Parameter>();
            foreach (var parameter in parameters)
            {
                _parameters.Add(new NamedParameter(parameter.Key, parameter.Value));
            }
            return Container.Resolve<IEnumerable<T>>(_parameters).ToArray();
        }
    }
    
    0 讨论(0)
  • 2021-01-31 03:12

    Yes, it is possible to pass only a sub set of parameters:

    public Contract(IPerson person, String name)
    { 
        this.Person = person;
        person.Name = name;
    }
    

    ....

    // this uses the person/name ctor. Person is factored and injected by the contianer
    List<Parameter> parameters = new List<Parameter>();
    parameters.Add(new NamedParameter("name", "cloe"));
    contract = scope.Resolve<IContract>(parameters);
    
    0 讨论(0)
提交回复
热议问题