Home > Software design >  Supplying runtime data to the Container inside a parallel for loop
Supplying runtime data to the Container inside a parallel for loop

Time:10-05

public class Factor1(int FactorX, int FactorY) : IFactors;
public class Factor2(int FactorX, int FactorY, int FZ) : IFactors;

public interface IFactors
{
    int FactorX { get; set; }
    int FactorY { get; set; }
}

public class BusinessLayerClass1
{
    IFactors _factorObj;
    public BusinessLayerClass1(IFactors factorObj)
    {
        _factorObj = factorObj;
    }
    public void Call() =>
        new BusinessLayerClass2(_factorObj).ShowFactors();
}

public class BusinessLayerClass2
{
    IFactors _factorObj;
    public BusinessLayerClass2(IFactors factorObj)
    {
        _factorObj = factorObj;
    }

    public void ShowFactors()
    {
        Debug.WriteLine(String.Concat(_factorObj.FactorX.ToString(), " - "  
         _factorObj.FactorY.ToString()));
    }
}

Main program

using SimpleInjector;

class Program
{
    public static void Main(string[] args)
    {
        List<Task> tasks = new List<Task>();
        for (var i = 0; i < 10; i  )
        {
            tasks.Add(Task.Factory.StartNew((num) =>
            {
                int num_ = (int)num;
                var factorObj = new Factor1() { FactorX = num_, FactorY = num_   1 };
                var container = new Container();
                var lifestyle = Lifestyle.Transient;
                container.Register<BusinessLayerClass1>(lifestyle);
                container.Register<BusinessLayerClass2>(lifestyle);
                container.RegisterInstance<IFactors>(factorObj);
                container.Verify();
                var BL = container.GetInstance<BusinessLayerClass1>();
                BL.Call();
            }, i));
        }
        Task.WaitAll(tasks.ToArray());
    }
}
  • Here, i implement Dependency injection to BusinessLayer classes to process factors(show values in that example).
  • Factor1 objects must be created at the beginning asynchronously using Task factory.
  • This works but i wonder if this is the right way to implement DI(for this example)?
  • If yes then, is there a better/convenient way to do that.
  • If no then, how should i implement DI regarding to this example?

CodePudding user response:

  • You are creating a new container instance inside a while loop. This is not advised because of the performance overhead this is giving in a real-life application (see the Simple Injector documentation for more info). Perhaps not so much an issue for a short-lived Console application, but certainly something to consider.
  • The reason you seem to be creating a many container instances is likely because the Factor classes consist of runtime data and you are injecting runtime data into your application components. This practice is discourages, as I explained here. The refernced article also explains what yo do instead, which is to "let runtime data flow through the method calls of constructed object graphs.".
  • In your case this either means
    1. Changing the BL.Call method to include that runtime data as input parameter, and later on call BL.Call(factorObj) or
    2. Defining an IFactorContext interface/class pair, register it as Scoped, and supply the runtime values to a resolved IFactorContext.

Concerning the second option, this might look like this:

// Definitions
public interface IFactorContext { IFactors Factors { get; } }
public class FactorContext : IFactorContext { public IFactors Factors { get; set; } }

// Register container once
var container = new Container();
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
container.Register<BusinessLayerClass1>();
container.Register<BusinessLayerClass2>();
container.Register<FactorContext>(Lifestyle.Scoped);
container.Register<IFactorContext, FactorContext>(Lifestyle.Scoped);
container.Verify();

// Operate loop many times
tasks.Add(Task.Factory.StartNew((num) =>
{
  using (AsyncScopedLifestyle.BeginScope(container))
  {
      int num_ = (int)num;
      var factorObj = new Factor1() { FactorX = num_, FactorY = num_   1 };
  
      // Always reuse the single container instance here.
      container.GetInstance<FactorContext>().Factor = factorObj;
  
      var BL = container.GetInstance<BusinessLayerClass1>();
      BL.Call();
  }
}, i));
  • Related