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
- Changing the
BL.Call
method to include that runtime data as input parameter, and later on callBL.Call(factorObj)
or - Defining an
IFactorContext
interface/class pair, register it asScoped
, and supply the runtime values to a resolvedIFactorContext
.
- Changing the
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));