单例 class 可以有非单例依赖吗?
Can a singleton class have a non-singleton dependency?
我想知道 IServiceCollection.AddDbContext()
如何添加我的 ApplicationDbContext
。我的猜测是 不是 作为单例添加的,并且每个请求都会创建一个新实例。
我正在实施自定义 ILoggerProvider
,它需要 ApplicationDbContext
的实例。
我的问题是:如果我的 ILoggerProvider
配置为单例,但它依赖于非单例的 ApplicationDbContext
会怎样?
是的,但是使用来自 ILogger 的 DbContext 并不是最好的主意。记录器和日志接收器是独立的实体。
从单例中使用作用域服务并不罕见。 Background services, which are registered as singletons. Consuming scoped services is explained in the section Consuming a scoped service in a background task.
就是这种情况
要使用作用域(或瞬态)服务,单例需要访问 IServiceProvider
。这通常作为构造函数依赖项传递。
public class MyHostedService : BackgroundService
{
private readonly ILogger<MyHostedService> _logger;
public MyHostedService(IServiceProvider services,
ILogger<MyHostedService> logger)
{
Services = services;
_logger = logger;
}
}
此 class 可用于使用 IServiceProvider.GetService or GetRequiredService 检索瞬态实例:
var service = Services.GetRequiredService<MyTransientService>();
要使用范围服务,单例需要显式创建范围并从该范围检索服务。当范围被处置时,任何范围内的实例也将被处置
using (var scope = Services.CreateScope())
{
var context =
scope.ServiceProvider
.GetRequiredService<MyDbContext>();
...
context.SaveChanges();
}
记录器和数据库
ILogger 不需要依赖于 DbContext。日志库使用单独的接口来发布和存储日志条目。 ILogger
实例用于 发布 日志条目,而不是存储它。存储日志事件是日志接收器或日志提供程序的工作 - 不同的日志库为此使用不同的名称。
.NET Core 有意提供了很少的日志接收器。有非常好的日志记录库,如 Serilog,.NET Core 团队无意重复他们的工作。他们确实为 Azure Application Insights 等 Microsoft 特定服务提供了一些接收器实现。
可以编写一个写入数据库的自定义日志接收器,但最好使用已经提供此功能并负责缓冲、异步操作等的现有库。写入数据库总是比写入数据库更昂贵写入本地文件,因此数据库接收器需要缓冲消息以减少对应用程序的影响。
可以轻松集成 Serilog 等现有库之一
public static int Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateLogger();
....
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog() // <-- Add this line
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
该库反过来提供像 Serilog.Sinks.SqlServer 这样的数据库接收器。
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.MSSqlServer(
connectionString: "Server=localhost;Database=LogDb;Integrated Security=SSPI;",
sinkOptions: new MSSqlServerSinkOptions { TableName = "LogEvents" })
.CreateLogger();
SQL 服务器接收器将创建日志 table 并开始向其中写入日志条目。 UseSerilog()
调用会将所有 ILogger
调用重定向到 Serilog 并最终重定向到数据库 table.
我想知道 IServiceCollection.AddDbContext()
如何添加我的 ApplicationDbContext
。我的猜测是 不是 作为单例添加的,并且每个请求都会创建一个新实例。
我正在实施自定义 ILoggerProvider
,它需要 ApplicationDbContext
的实例。
我的问题是:如果我的 ILoggerProvider
配置为单例,但它依赖于非单例的 ApplicationDbContext
会怎样?
是的,但是使用来自 ILogger 的 DbContext 并不是最好的主意。记录器和日志接收器是独立的实体。
从单例中使用作用域服务并不罕见。 Background services, which are registered as singletons. Consuming scoped services is explained in the section Consuming a scoped service in a background task.
就是这种情况要使用作用域(或瞬态)服务,单例需要访问 IServiceProvider
。这通常作为构造函数依赖项传递。
public class MyHostedService : BackgroundService
{
private readonly ILogger<MyHostedService> _logger;
public MyHostedService(IServiceProvider services,
ILogger<MyHostedService> logger)
{
Services = services;
_logger = logger;
}
}
此 class 可用于使用 IServiceProvider.GetService or GetRequiredService 检索瞬态实例:
var service = Services.GetRequiredService<MyTransientService>();
要使用范围服务,单例需要显式创建范围并从该范围检索服务。当范围被处置时,任何范围内的实例也将被处置
using (var scope = Services.CreateScope())
{
var context =
scope.ServiceProvider
.GetRequiredService<MyDbContext>();
...
context.SaveChanges();
}
记录器和数据库
ILogger 不需要依赖于 DbContext。日志库使用单独的接口来发布和存储日志条目。 ILogger
实例用于 发布 日志条目,而不是存储它。存储日志事件是日志接收器或日志提供程序的工作 - 不同的日志库为此使用不同的名称。
.NET Core 有意提供了很少的日志接收器。有非常好的日志记录库,如 Serilog,.NET Core 团队无意重复他们的工作。他们确实为 Azure Application Insights 等 Microsoft 特定服务提供了一些接收器实现。
可以编写一个写入数据库的自定义日志接收器,但最好使用已经提供此功能并负责缓冲、异步操作等的现有库。写入数据库总是比写入数据库更昂贵写入本地文件,因此数据库接收器需要缓冲消息以减少对应用程序的影响。
可以轻松集成 Serilog 等现有库之一public static int Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateLogger();
....
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog() // <-- Add this line
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
该库反过来提供像 Serilog.Sinks.SqlServer 这样的数据库接收器。
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.MSSqlServer(
connectionString: "Server=localhost;Database=LogDb;Integrated Security=SSPI;",
sinkOptions: new MSSqlServerSinkOptions { TableName = "LogEvents" })
.CreateLogger();
SQL 服务器接收器将创建日志 table 并开始向其中写入日志条目。 UseSerilog()
调用会将所有 ILogger
调用重定向到 Serilog 并最终重定向到数据库 table.