我无法解决 DocumentMoveData.TemplateFiles 方法的查询超时问题。该方法由从同一基类派生的三个 BackgroundServices 调用。服务之间的唯一区别是查询参数。服务使用 IServiceScopeFactory 创建范围,从范围中获取 IDocumentMoveData,然后调用 TemplateFiles 方法。该方法使用 DBContext 对 SQL Server 执行 LINQ 查询。在 SQL 管理控制台中执行相同的查询只需不到 1 秒。
我应该采取什么步骤来解决这个问题?
public class DocumentMoveService : BackgroundService {
private IServiceScopeFactory _services;
public DocumentMoveService(IServiceScopeFactory services) {
_services = services;
}
}
public class DocumentMoveServiceOne : DocumentMoveService{}
public class DocumentMoveServiceTwo : DocumentMoveService{}
I omit other parameters in DocumentMoveService for brevity, none of the parameters are using DBContext.
DocumentMoveService has ExecuteAsync that is used by all three classes, only the query parameter is different.
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
using (var scope = _services.CreateScope())
{
var scopedServices = scope.ServiceProvider;
docV1Factory = scopedServices.GetService<IDocumentMoveData>();
– the following call uses DBContext see DocumentMoveData constructor below
filesToProcess = await docV1Factory.TemplateFiles(10, _storageConfig.FileType);
foreach (DocumentInfo f in filesToProcess)
{
…
await docV1Factory.Save(f, destfile, (int)_storageConfig.FileType);
}
//
public interface IDocumentMoveData
{
Task Save(DocumentInfo docinfo, IStorageFile savedfile, int ftype);
Task<List<DocumentInfo>> TemplateFiles(int count, eFormsModel.DataObjects.DocumentFile.TypeEnum ftype);
}
public class DocumentMoveData : IDocumentMoveData {
private readonly DBContext _context;
public DocumentMoveData(DBContext context)
{
_context = context;
}
public async Task Save(DocumentInfo docinfo, IStorageFile savedfile, int ftypeid)
{
int storageid = await GetStorageId(savedfile.Folder);
DocTemplateFile newret = new DocTemplateFile()... // init code removed for brevity
_context.DocumentTemplateFiles.Add(newret);
await _context.SaveChangesAsync();
}
public async Task<List<DocumentInfo>> TemplateFiles(int count, eFormsModel.DataObjects.DocumentFile.TypeEnum ftype)
{
try
{
var query = _context.DocumentFiles
.Include(o=> o.Document)
.Where(df => df.FileTypesId == Convert.ToInt32(ftype))
.Where(c => !_context.DocumentTemplateFiles.Any(cc => cc.OriginalDocId == c.DocumentsId))
.Select(df => new DocumentInfo
{
DocumentFileId = df.Id,
FileName = df.FileName,
DocumentId = df.DocumentsId,
UserId = _context.Documents
.Where(d => d.Id == df.DocumentsId)
.Select(d => d.UploadedByUserId)
.FirstOrDefault(),
CustomerId = _context.Users
.Where(u => u.LoginId == _context.Documents
.Where(d => d.Id == df.DocumentsId)
.Select(d => d.UploadedByUserId)
.FirstOrDefault())
.Select(u => u.CustomersId)
.FirstOrDefault(),
WhenCreated = df.Document.UploadedDate
})
.OrderByDescending(df => df.DocumentId)
.Take(count);
List<DocumentInfo> documentFileInfos = query.ToList();
return documentFileInfos;
}
}
Initialization in Program.cs
hostBuilder.ConfigureServices(services =>
{
services.AddDbContext<DBContext>(options =>
{
var t = config.GetConnectionString("connstr");
options.UseSqlServer(t);
}, ServiceLifetime.Scoped);
services.AddScoped<DBContext>();
services.AddScoped<IDocumentMoveData, DocumentMoveData>();
services.AddHostedService<DocumentMoveService>(serviceProvider =>
{
var logger = serviceProvider.GetRequiredService<ILogger<DocumentMoveService>>();
var scopeFactory = serviceProvider.GetService<IServiceScopeFactory>();
StorageTypeByTaskType[configFType.FileType] = typeof(DocumentMoveService);
return new DocumentMoveService(logger, scopeFactory, configFType);
// same for two additional backgroundservices
services.AddHostedService<DocumentMoveServiceOne>(serviceProvider =>
services.AddHostedService<DocumentMoveServiceTwo>(serviceProvider =>
..
});
这个电话:
services.AddDbContext<DBContext>(options => { var t = config.GetConnectionString("connstr"); options.UseSqlServer(t); });
查询降级发生在大约 100 次迭代之后。通过这个电话:
services.AddDbContext<DBContext>(options => { var t = config.GetConnectionString("connstr"); options.UseSqlServer(t); }, ServiceLifetime.Scoped);
立即发生
生成的查询:
declare @__p_1 int, @__ToInt32_0 int
select @__p_1 = 10, @__ToInt32_0 = 2
SELECT [t].[ID] AS [DocumentFileId], [t].[FileName], [t].[Documents_ID] AS [DocumentId], COALESCE((
SELECT TOP(1) [d2].[UploadedBy_User_ID]
FROM [Documents] AS [d2]
WHERE [d2].[ID] = [t].[Documents_ID]), 0) AS [UserId], COALESCE((
SELECT TOP(1) [u].[Customers_ID]
FROM [Users] AS [u]
WHERE [u].[login_id] = COALESCE((
SELECT TOP(1) [d3].[UploadedBy_User_ID]
FROM [Documents] AS [d3]
WHERE [d3].[ID] = [t].[Documents_ID]), 0)), 0) AS [CustomerId], [d1].[UploadedDate] AS [WhenCreated]
FROM (
SELECT TOP(@__p_1) [d].[ID], [d].[Documents_ID], [d].[FileName]
FROM [DocumentFiles] AS [d]
WHERE [d].[FileTypes_ID] = @__ToInt32_0 AND NOT (EXISTS (
SELECT 1
FROM [DocumentTemplateFiles] AS [d0]
WHERE [d0].[OriginalDocId] = [d].[Documents_ID]))
ORDER BY [d].[Documents_ID] DESC
) AS [t]
INNER JOIN [Documents] AS [d1] ON [t].[Documents_ID] = [d1].[ID]
ORDER BY [t].[Documents_ID] DESC
文件:
CREATE TABLE [dbo].[Documents](
[ID] [int] IDENTITY(1,1) NOT NULL,
...*(other fields)*
CONSTRAINT [PK_Documents] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
文档文件:
CREATE TABLE [dbo].[DocumentFiles](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Documents_ID] [int] NOT NULL,
[FileTypes_ID] [int] NOT NULL,
*.. (other fields)*
CONSTRAINT [PK_Files] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]