我在 ASP.NET Core 6 Web API 中遇到
DbContext
的问题,获取异常连接处置或关闭。因为我在整个应用程序中使用相同的 DbContext
,所以当数据库相关操作第一次完成时,我会遇到这个问题,并且我尝试使用 DbContext
执行其他操作,关闭或处置异常连接。
我知道这是因为
DbContext
的生命周期,但是我尝试使用 DbContext
在单独的线程中使用新的 serviceProvider
执行第二个操作,但仍然存在相同的问题,第一个操作执行,而第二个操作总是失败。
这是我的经理代码:
public class ClientServices : IClientServices
{
private readonly Context _dbContext;
private readonly IHttpContextAccessor _httpContextAccessor; // Injected IHttpContextAccessor
private readonly IConfiguration _configuration;
private readonly ApiClient _apiClient;
private readonly LoggingService _logger;
private readonly AzureFileShareService _azurefileshareservice;
private readonly IServiceProvider _serviceProvider;
public ClientServices(Context dbContext, IHttpContextAccessor httpContextAccessor,
IConfiguration configuration, AzureFileShareService azurefileshareservice, ApiClient apiClient,
LoggingService logger, IServiceProvider serviceProvider)
{
_dbContext = dbContext;
_httpContextAccessor = httpContextAccessor;
_configuration = configuration;
_azurefileshareservice = azurefileshareservice;
_apiClient = apiClient;// new ApiClient(_configuration);
_logger = logger;
_serviceProvider = serviceProvider;
}
public async Task<byte[]> GetReport(string tempNo, string tempype, Guid UserId)
{
byte[] report = null;
var requestLogDto = new RptRequestLog();
requestLogDto.UserId = UserId;
requestLogDto.RequestTime = DateTime.Now;
var obj = new
{
tempNo = tempNo,
tempype = tempype
};
var customHeaders = new Dictionary<string, string>
{
// some values...
};
var response = await _apiClient.PostAsync(_configuration["client:URL"], obj, customHeaders);
if (response.Data is byte[] byteArray)
{
report = byteArray;
}
var responseLogDto = new ResponseLog();
responseLogDto.Doc = report;
responseLogDto.ResponseTime = DateTime.Now;
Task.Run(async () =>
{
var docUlr = await _uploadAzureFilestorage(params);
responseLogDto.DocUrl = docUlr;
loggingInSeparateThread(report, tempNo, requestLogDto, responseLogDto, "RptRequestLog", "RptResponseLog");
});
return report;
}
public async Task loggingInSeparateThread(byte[] report, string tempNo, dynamic requestLogDto, dynamic responseLogDto,
string reqlogTable, string respLogtable)
{
using (var scope = _serviceProvider.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<Context>();
JsonSerializerOptions options = new JsonSerializerOptions
{
ReferenceHandler = ReferenceHandler.Preserve,
WriteIndented = true // Optional: for better readability
};
Log(requestLogDto, responseLogDto, reqlogTable, respLogtable, dbContext);
AuditHistory(System.Text.Json.JsonSerializer.Serialize(requestLogDto, options),
System.Text.Json.JsonSerializer.Serialize(responseLogDto, options),
requestLogDto.HshUserId, requestLogDto.RequestTime.Value, DateTime.Now, dbContext);
}
}
public async Task Log(dynamic requestBody, dynamic responseBody, string requesTable, string responseTable, Context context)
{
var requestLogId = await _logger.LogToTable(requestBody, requesTable, context);
responseBody.ReptRequestLogId = requestLogId;
_logger.LogToTable(responseBody, responseTable, context);
}
public async Task AuditHistory(string requestBody, string responseBody, Guid UserId,
DateTime requestTime, DateTime responseTime, Context context)
{
var auditHistory = new AuditHistory();
auditHistory.RequestBody = requestBody;
auditHistory.RequestTime = requestTime;
auditHistory.ResponseBody = responseBody;
auditHistory.ResponseTime = responseTime;
await _logger.LogAuditHistory(auditHistory, "AuditHistory", context);
}
}
这是日志服务:
public class LoggingService
{
private readonly Context context;
public LoggingService(Context dbContext)
{
context = dbContext;
}
public async Task<Guid> LogToTable<T>(T model, string tableName,Context context) where T : class
{
try
{
var dbSet = context.Set<T>();
dbSet.Add(model);
await context.SaveChangesAsync();
var idProperty = typeof(T).GetProperty("Id");
if (idProperty != null)
{
var newId = (Guid)idProperty.GetValue(model);
return newId;
}
}
catch (Exception ex)
{
}
return Guid.Empty;
}
}
任何人都可以解释如何实施通用解决方案来做到这一点吗?
供参考,当日志方法调用在数据库中插入记录时,第一个操作成功执行(在数据库中插入记录),但当它第二次插入时,请考虑这一点:
_logger.LogToTable(responseBody, responseTable, context);
然后我得到异常连接关闭/处置。
这里的代码不起作用:
public class LoggingService
{
private readonly Context context;
public LoggingService(Context dbContext)
{
context = dbContext;
}
public async Task<Guid> LogToTable<T>(T model, string tableName,Context context) where T : class
{
try
{
var dbSet = context.Set<T>();
// ...
在后台任务中执行操作时,不能依赖注入的 DbContext,它需要限定在任务本身的范围内。解决这个问题的最简单方法是创建一个 DbContextFactory 并将其注入到具有后台任务类实例的类中,然后后台任务可以使用它来实例化
DbContext
的作用域实例:
public class LoggingService
{
private readonly IAppDbContextContextFactory contextFactory;
public LoggingService(IAppDbContextFactory contextFactory)
{
this.contextFactory = contextFactory;
}
public async Task<Guid> LogToTable<T>(T model, string tableName,Context context) where T : class
{
try
{
using context = contextFactory.Create();
var dbSet = context.Set<T>();
dbSet.Add(model);
await context.SaveChangesAsync();
var idProperty = typeof(T).GetProperty("Id");
if (idProperty != null)
{
var newId = (Guid)idProperty.GetValue(model);
return newId;
}
}
catch (Exception ex)
{
}
return Guid.Empty;
}
}
这应该可以解决问题。例如,具有
async
操作的控制器可以使用范围仅限于 Web 请求的 DbContext
,因为这些操作将等待并在 Web 请求范围内执行,其中 DbContext
将保持活动状态。如果您将其交给后台工作人员并使用相同的 DbContext
实例对其进行初始化,您可能会遇到各种问题,包括后台工作人员和 Web 请求同时尝试访问 DbContext 的可能性(跨线程问题,因为 DbContext 是非线程安全)或 Web 请求结束的情况,触发 DbContext 的处置并且后台线程尝试访问对其的引用。