I have a form. It contains things like grids. Users do things with these grids and what they do with them is sent to a SQL server. Clearly, the server is a volatile dependency and should be dependency injected.
Of course, it would be silly to have the form know about my business logic, so all of the business logic to do with the grids is in individual classes. Let's call where these classes live my "business logic layer" and where my form the lives the "UI layer". Since my classes in the business logic layer need to know about the server and the server is a volatile dependency, those classes should take the relevant details about the server in their constructor. For example, the class holding the business logic for FooCells
in the grid (let's call it FooCellsHandler
) should take an IFooSqlRepository
in its constructor and that IFooSqlRepository
parameter should be passed a FooSqlRepository
.
So far, our composition root looks something like this:
private static RootType CompRoot(string SqlConnString)
{
return
new Form1(
new FooCellsHandler(
new FooSqlRepository(
new SqlContext(SqlConnString)
)
)
)
;
}
And although the indentation is ugly, it's all fine... As long as the only argument that FooCellHandler
needs is the IFooSqlRepository
. If it needs extra arguments at runtime, let's say CellHeader
and CellValue
, then we can't have those in our composition root. We therefore need to make a factory. Our composition root will still be the same, but with FooCellsHandler
replaced with FooCellsHandlerFactory
. This factory object will only constructed once in the entire application. Here is one such possible FooCellsHandler
and its factory.
public class TimeCellHandler
{
private readonly int _value;
private readonly string _header;
private readonly ITimeCellSqlRepository _repo;
public TimeCellHandler(int CellValue, string CellHeader, ITimeCellSqlRepository Repo)
{
_value = CellValue;
_header = CellHeader;
_repo = Repo;
}
private void FormatForServer()
{
// Helper method used by others.
}
public void DeleteFromServer()
{
// Deletes values corresponding to this time cell from the server.
}
public void InsertToServer()
{
// Inserts values corresponding to this time cell to the server.
}
}
public class TimeCellHandlerFactory
{
private readonly ITimeCellSqlRepository _repo;
public TimeCellHandlerFactory(ITimeCellSqlRepository Repo)
{
_repo = Repo;
}
public TimeCellHandler MakeHandler(int CellValue, string CellHeader)
{
return TimeCellHandler(CellValue, CellHeader, _repo);
}
}
Is having many build-once factories a sign of bad dependency injection design? I see many reasons to be suspicious of it. Because my form handles many different things, I can imagine that my composition root will contain many such factories and begin to look something like the below. This seem ugly, repeats my SQL information a great many times, and puts many layers of indirection between what my form is supposed to do and how it is being done.
private static RootType CompRoot2(string SqlConnString)
{
return
new Form1(
new FooCellsHandlerFactory(
new FooSqlRepository(
new SqlContext(SqlConnString)
)
),
new BarCellsHandlerFactory(
new BarSqlRepository(
new SqlContext(SqlConnString)
)
),
new OtherCellsHandlerFactory(
new OtherSqlRepository(
new SqlContext(SqlConnString)
)
),
new MyColumnHandlerFactory(
new MyColumnSqlRepository(
new SqlContext(SqlConnString)
)
),
new EdgeCaseHandlerFactory(
new EdgeCaseSqlRepository(
new SqlContext(SqlConnString)
)
),
)
;
}
Microsoft.Extensions.DependencyInjection
: learn.microsoft.com/en-us/dotnet/core/extensions/…). Also, if you're using EF Core, then there's a further useful extension method to clean it up a little more: learn.microsoft.com/en-us/ef/core/dbcontext-configuration (Works well with Configuration from appSettings.json too)new Form1(new(new(new(SqlConnectionString),new(new(new(SqlConnectionString),new(new(new(SqlConnectionString),new(new(new(SqlConnectionString),new(new(new(SqlConnectionString))