简体   繁体   中英

How can I pass a parameter to a constructor using Autofac

I'm sure I've missed something here.

I am trying to convert non DI code to use autofac. I have a class which talks to hardware which takes an IP address and abstracts away the interface. I have a high level class that configures and operates the hardware. I'm trying to work out how I can instantiate multiple copies of this class without passing in a copy of the DI container.

string FirstIP, SecondIP;
//Set values from config/user etc.

var FirstDevice = new HighLevelIF(new LowLevelIF(FirstIP));
var SecondDevice = new HighLevelIF(new LowLevelIF(SecondIP));

If I only wanted one device object, it seems pretty trivial to wire this up in Autofac. But to have two instantiations with different addresses which I'll only know at runtime, I can't see a way to do this with autofac. Only way that I can see is to write a LowLevelIF Factory that I pass to the method, which I then instantiate

var FirstDevice = new HighLevelIFFactory.Create
                      (LowLevelIFFactory.Create
                      (FirstIP));

You can use the .WithParameter() method.

For example:

var builder = new ContainerBuilder();
builder.RegisterType<MyType>().As<IMyInterface>().WithParameter("paramName", "paramValue");

Edit: just re-reading your question. Possibly you want to name your registrations, and then get the appropriate one?

You can also do this:

var builder = new ContainerBuilder();
builder.RegisterType<MyType>().Named<IMyInterface>("MyName");
builder.RegisterType<MyType>().Named<IMyInterface>("MyOtherName");

var container = builder.Build();

// later
container.ResolveNamed<IMyInterface>("MyName"); //gets MyName registration

If you need these IPs to be resolved on demand and they are likely to change at runtime, abstract factory is your best bet.

If you have a set list of addresses that are loaded up at application start and you just need to switch between those instances in the most efficient way, you can use the strategy pattern , which allows you to avoid injecting the container into your class.

Strategy Example

var builder = new ContainerBuilder();
// Register all instances of IDiscountCalculator
builder.RegisterAssemblyTypes(this.GetType().Assembly)
       .Where(t => typeof(IDiscountCalculator).IsAssignableFrom(t));
builder.Register<DiscountStrategy>().As<IDiscountStrategy>();
var container = builder.Build();
var strategy = container.Resolve<IDiscountStrategy>();

Console.WriteLine(strategy.GetDiscount("Regular", 10)); // 0
Console.WriteLine(strategy.GetDiscount("Normal", 10)); // 1
Console.WriteLine(strategy.GetDiscount("Special", 10)); // 5

which depends on the following types:

public interface IDiscountStrategy 
{
    decimal GetDiscount(string userType, decimal orderTotal);
}

public class DiscountStrategy : IDiscountStrategy
{
    private readonly IEnumerable<IDiscountCalculator> _discountCalculators;

    public DiscountStrategy(IEnumerable<IDiscountCalculator> discountCalculators)
    {
        _discountCalculators = discountCalculators;
    }

    public decimal GetDiscount(string userType, decimal orderTotal)
    {
        var calculator = _discountCalculators.FirstOrDefault(x => x.AppliesTo(userType));
        if (calculator == null) return 0;
        return calculator.CalculateDiscount(orderTotal);
    }
}

public interface IDiscountCalculator
{
    bool AppliesTo(string userType);
    decimal CalculateDiscount(decimal orderTotal);
}

public class NormalUserDiscountCalculator : IDiscountCalculator
{
    public bool AppliesTo(string userType)
    {
        return userType == "Normal";
    }

    public decimal CalculateDiscount(decimal orderTotal)
    {
        return orderTotal * 0.1m;
    }
}

public class SpecialUserDiscountCalculator : IDiscountCalculator
{
    public bool AppliesTo(string userType)
    {
        return userType == "Special";
    }

    public decimal CalculateDiscount(decimal orderTotal)
    {
        return orderTotal * 0.5m;
    }
}

Note that using an AppliesTo method also means you can make a strategy that selects multiple instances that apply to a specific purpose, acting like a filter.

Just for reference, I created factory methods, passed those into the constructors (which autofac will wire for you automatically).

Then I had an "initialise" method which took the camera IP parameter and used the factories to instantiate my objects with the appropriate internal settings. Worked well for me, but I'm sure that the strategy pattern would have been valid too.

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