要避免在每個子類別中手動傳遞 ILogger 並呼叫基類的構造函數,你可以採用一個簡單的策略來減少重複的工作:利用依賴注入來自動解決這些依賴,以及避免在每個子類中重寫相同的構造函數邏輯。
方法一:將 DapperSql 的 ILogger 依賴改為由 DI 容器直接注入
你可以讓 DapperSql 自己解決 ILogger 依賴,而不需要在每個子類中顯式注入。
修改 DapperSql 構造函數
你可以使用 IServiceProvider 或 IHttpContextAccessor 來取得 DI 容器中的 ILogger 實例,這樣就不需要在子類中顯式傳入 ILogger。
csharp
複製程式碼
public class DapperSql : BaseClass, IDapperSql where TEntity : class
{
///
/// Dapper 物件
///
///
public DapperRepository dpr { get; set; } = new DapperRepository();
/// <summary>
/// Logger 物件
/// </summary>
protected readonly ILogger<DapperSql<TEntity>> _logger;
/// <summary>
/// Entity Object
/// </summary>
public TEntity EntityObject { get { return (TEntity)Activator.CreateInstance(typeof(TEntity)); } }
/// <summary>
/// Entity Name
/// </summary>
public string EntityName { get { return typeof(TEntity).Name; } }
/// <summary>
/// 建構子
/// </summary>
/// <param name="serviceProvider">DI 容器</param>
public DapperSql(IServiceProvider serviceProvider)
{
// 從 DI 容器中自動解析 Logger
_logger = serviceProvider.GetRequiredService<ILogger<DapperSql<TEntity>>>();
}
}
方法二:將 DapperSql 透過 DI 注入,不需重複調用基類構造函數
透過修改 Program.cs 中的服務註冊來解決,並且你不需要在子類中顯式地處理 ILogger,只需注入 DapperSql 即可:
csharp
複製程式碼
// 在 Program.cs 中,註冊 DapperSql 類別,並讓 DI 容器解決 Logger 依賴
builder.Services.AddScoped(typeof(DapperSql<>));
子類別不需處理 ILogger 的情況
現在你的子類別中無需顯式調用基類的構造函數,也不需要手動處理 ILogger 了:
csharp
複製程式碼
namespace escale.Areas.Admin.Models
{
public class z_sqlDepartments : DapperSql
{
public z_sqlDepartments(IServiceProvider serviceProvider)
: base(serviceProvider) // DI 容器將自動傳遞 ILogger
{
OrderByColumn = SessionService.SortColumn;
OrderByDirection = SessionService.SortDirection;
DefaultOrderByColumn = "Departments.DeptNo";
DefaultOrderByDirection = "ASC";
if (string.IsNullOrEmpty(OrderByColumn)) OrderByColumn = DefaultOrderByColumn;
if (string.IsNullOrEmpty(OrderByDirection)) OrderByDirection = DefaultOrderByDirection;
}
}
}
自動處理多個子類別
這樣每個子類別只需將 IServiceProvider 傳遞給基類,而不需要顯式處理 ILogger 的注入問題。只要你透過 DI 容器註冊了 DapperSql,每個子類別都能自動處理這個依賴。
如果你的專案中有許多類似的子類別,你就不需要再為每一個類別手動處理 ILogger 的問題,這樣可以大大減少重複的工作。
你說:
原本有寫public DapperSql(ILogger<DapperSql> logger)
{
_logger = logger;
}和 ///
/// 建構子
///
/// DI 容器
public DapperSql(IServiceProvider serviceProvider)
{
// 從 DI 容器中自動解析 Logger
_logger = serviceProvider.GetRequiredService<ILogger<DapperSql>>();
}
}差在哪裡
ChatGPT 說:
ChatGPT
這兩個版本的構造函數處理 ILogger 依賴注入的方式不同,具體差別在於誰來提供 ILogger 和如何獲取 ILogger。讓我們來詳細說明:
版本一:直接注入 ILogger
csharp
複製程式碼
public DapperSql(ILogger<DapperSql> logger)
{
_logger = logger;
}
特點:
ILogger 是直接通過構造函數注入的。
當你在 Program.cs 註冊 DapperSql 類別時,依賴注入容器會自動解析並傳遞 ILogger 實例。
簡單明了:直接通過依賴注入容器提供 ILogger。
使用場景:
適合你已經為 DapperSql 註冊了具體的 ILogger 提供者,並希望每個子類別在創建時能夠從外部顯式地傳入 ILogger。
比較常見的做法,因為這樣寫的結構更加清晰,所有依賴都明確地通過構造函數傳入。
缺點:
每個子類別都需要傳入 ILogger。當你有很多子類別時,必須一一傳遞 ILogger,增加了重複的工作量。
版本二:使用 IServiceProvider 自動解析 ILogger
csharp
複製程式碼
public DapperSql(IServiceProvider serviceProvider)
{
// 從 DI 容器中自動解析 Logger
_logger = serviceProvider.GetRequiredService<ILogger<DapperSql>>();
}
特點:
ILogger 是從 IServiceProvider 中動態解析的。
這種方式使用 IServiceProvider 作為依賴注入容器,並通過 serviceProvider.GetRequiredService 方法在運行時動態解析所需的依賴。
自動解析依賴:不需要在構造函數中顯式地提供 ILogger,只要有一個 DI 容器(IServiceProvider),它就能自動解析依賴。
使用場景:
適合你有很多子類別,不想手動傳入 ILogger 或其他依賴時,讓 DI 容器自動解決所有依賴。
減少了手動注入的麻煩。當系統越來越複雜,這種方式可以讓構造函數更簡潔。
缺點:
隱式依賴解析:依賴被隱式解析,而不是顯式傳遞,這在某些情況下可能會讓依賴管理變得不夠直觀。
依賴於 IServiceProvider:使用這種方式會耦合到 IServiceProvider,如果你不使用 DI 容器,或者希望更加靈活的依賴傳遞方式,這樣的設計可能會限制靈活性。
總結
版本一(直接注入 ILogger):
優點:清晰直觀,適合單一類別直接管理依賴。
缺點:每個子類別都需要手動傳遞依賴,重複勞動。
版本二(使用 IServiceProvider 解析 ILogger):
優點:減少重複勞動,DI 容器自動解析依賴,適合大規模子類別的場景。
缺點:依賴隱式解析,耦合到 IServiceProvider,不夠靈活。
如果你有很多子類別,版本二 會是更好的選擇,因為你不需要手動在每個子類別中傳入 ILogger,由 DI 容器自動解析依賴即可。
你說:
依賴於 IServiceProvider:使用這種方式會耦合到 IServiceProvider,如果你不使用 DI 容器,或者希望更加靈活的依賴傳遞方式,這樣的設計可能會限制靈活性。示範解釋
ChatGPT 說:
ChatGPT
依賴於 IServiceProvider 是指當你在類別中使用 IServiceProvider 來解析依賴時,這個類別的運行就依賴於依賴注入(DI)容器。如果你沒有 DI 容器的支援,或者想要靈活地手動管理依賴,那麼這樣的設計可能會變得不夠靈活。
示範解釋
假設你使用以下代碼來依賴 IServiceProvider 解析依賴:
csharp
複製程式碼
public class DapperSql
{
private readonly ILogger<DapperSql> _logger;
public DapperSql(IServiceProvider serviceProvider)
{
// 從 DI 容器中動態解析 ILogger
_logger = serviceProvider.GetRequiredService<ILogger<DapperSql<TEntity>>>();
}
public void Log(string message)
{
_logger.LogInformation(message);
}
}
這段代碼使用了 IServiceProvider,當應用程式啟動時,它會依賴於依賴注入容器來動態解析 ILogger。換句話說,你必須有一個 DI 容器來解析 ILogger。
耦合到 IServiceProvider 的問題
如果你想在不使用 DI 容器的情況下使用這個類別,會遇到一些困難。比如在一個傳統的應用程式,沒有設定 DI 容器的時候,你需要手動管理依賴。
不使用 DI 容器的情況
假設你想在沒有 DI 容器的情況下使用 DapperSql,這樣的設計會導致問題,因為 IServiceProvider 不是你能輕易手動創建的,你會發現很難實例化 DapperSql。
例如,下面的代碼在沒有 DI 容器的情況下是無法運行的:
csharp
複製程式碼
public class Test
{
public void TestMethod()
{
var dapperSql = new DapperSql(); // 錯誤,沒有傳入 IServiceProvider
dapperSql.Log("Testing logging...");
}
}
由於 DapperSql 的構造函數需要 IServiceProvider,但在這裡沒有 DI 容器的情況下,你不能手動提供這個參數,導致程式無法正常執行。
更靈活的設計
如果你希望更加靈活,不依賴於 DI 容器,你可以使用構造函數注入,顯式地傳入 ILogger,例如這樣:
csharp
複製程式碼
public class DapperSql
{
private readonly ILogger<DapperSql> _logger;
public DapperSql(ILogger<DapperSql<TEntity>> logger)
{
_logger = logger;
}
public void Log(string message)
{
_logger.LogInformation(message);
}
}
在這種情況下,你可以靈活地手動傳入 ILogger 而不依賴於 DI 容器:
csharp
複製程式碼
public class Test
{
public void TestMethod()
{
var logger = new LoggerFactory().CreateLogger<DapperSql>(); // 手動創建 logger
var dapperSql = new DapperSql(logger); // 手動傳入 logger
dapperSql.Log("Testing logging...");
}
}
這樣的設計更靈活,因為你不再依賴 IServiceProvider,而是直接傳入依賴,無論你是否使用 DI 容器,都可以靈活地管理依賴。
已经在 Program.cs 中添加了 builder.Services.AddScoped(typeof(DapperSql<>));,并且依然收到类似于 “There is no argument given that corresponds to the required parameter 'serviceProvider'” 的错误,这可能是因为你没有在实例化 z_sqlDepartments 类时正确处理构造函数继承,或者在注册 DapperSql<> 时存在问题。
关键问题:
你提到的 z_sqlDepartments 类没有通过构造函数调用基类 DapperSql,所以 IServiceProvider 无法被注入到基类中。
解决方案:
在派生类中调用基类构造函数: 因为 z_sqlDepartments 继承了 DapperSql<Departments>,而基类 DapperSql<TEntity> 需要一个 ILogger 或 IServiceProvider,所以你必须在 z_sqlDepartments 的构造函数中显式调用基类构造函数,并传递这些依赖项。
更新你的 z_sqlDepartments 构造函数,确保它调用了基类的构造函数,并传递依赖项:
csharp
複製程式碼
namespace escale.Areas.Admin.Models
{
public class z_sqlDepartments : DapperSql<Departments>
{
public z_sqlDepartments(IServiceProvider serviceProvider)
: base(serviceProvider) // 调用基类构造函数并传递 serviceProvider
{
OrderByColumn = SessionService.SortColumn;
OrderByDirection = SessionService.SortDirection;
DefaultOrderByColumn = "Departments.DeptNo";
DefaultOrderByDirection = "ASC";
if (string.IsNullOrEmpty(OrderByColumn)) OrderByColumn = DefaultOrderByColumn;
if (string.IsNullOrEmpty(OrderByDirection)) OrderByDirection = DefaultOrderByDirection;
}
}
}
这就确保在实例化 z_sqlDepartments 时,IServiceProvider 正确地传递给了 DapperSql<TEntity>。
确保依赖注入的正确使用: 确保你是通过依赖注入的方式使用 z_sqlDepartments,而不是手动创建实例。
例如,如果你在控制器中使用:
csharp
複製程式碼
public class DepartmentsController : Controller
{
private readonly z_sqlDepartments _zSqlDepartments;
public DepartmentsController(z_sqlDepartments zSqlDepartments)
{
_zSqlDepartments = zSqlDepartments;
}
public IActionResult Index()
{
// 使用 _zSqlDepartments 做一些操作
return View();
}
}
如果你是手动实例化 z_sqlDepartments,则 DI 容器无法为它自动注入依赖项。