我正在开发一个带有 EF core 和 sqlite db 的 Winui/c# 应用程序。 我正在一个包含多个表的数据库中搜索,每个表包含 9M 条记录 (9,000,000)。
对于这个繁重的搜索,我使用了 Task.Run 和 async/await。现在有一个问题,当我搜索数据时,我可以看到应用程序运行正常并显示结果。 但如果我再做一次搜索,我会得到一些例外:
抛出异常:WinRT.Runtime.dll 中的“System.InvalidOperationException” 抛出异常:WinRT.Runtime.dll 中的“System.ArgumentOutOfRangeException”
我将所有代码放入 Dispatcher.TryEnqueue 并修复了问题,但是 Task.Run 没有任何效果并且 ui 被阻塞。
那么我该如何解决这个问题呢?看来db在Task.Run中无法正常工作
await Task.Run(async () =>
{
await PerformSearchAsync(query, progress, cancellationTokenSource.Token);
});
private Dictionary<TableType, Func<DbContext, IQueryable<object>>> tableMappings = new Dictionary<TableType, Func<DbContext, IQueryable<object>>>
{
{ TableType.T935, db => db.Set<Person935>() },
{ TableType.T936, db => db.Set<Person936>() },
{ TableType.T937, db => db.Set<Person937>() },
{ TableType.T938, db => db.Set<Person938>() },
{ TableType.T939, db => db.Set<Person939>() },
{ TableType.T93033, db => db.Set<Person93033>() }
};
public async Task PerformSearchAsync(string query, IProgress<int> progress, CancellationToken cancellationToken)
{
var db = new IFDBDbContext();
var tableQueries = new List<IAsyncEnumerable<object>>();
var tableType = GetTableType(SearchQuery);
if (tableMappings.TryGetValue(tableType, out var dbQuery))
{
var dbSet = dbQuery(db);
tableQueries.Add(GetSearchResultsAsync(dbSet, query, cancellationToken));
}
// Track progress
int totalTables = tableQueries.Count;
int completedTables = 0;
int completedItems = 0;
foreach (var tableQuery in tableQueries)
{
await foreach (var item in tableQuery.WithCancellation(cancellationToken))
{
completedItems++;
dispatcherQueue.TryEnqueue(() =>
{
DataList.Add(item);
_tmpCompletedItems = completedItems;
});
}
completedTables++;
progress.Report((completedTables * 100) / totalTables);
}
dispatcherQueue.TryEnqueue(() =>
{
ShowStatus(DataList.Count);
});
}
private async IAsyncEnumerable<object> GetSearchResultsAsync(IQueryable<object> dbSet, string query, [EnumeratorCancellation] CancellationToken cancellationToken)
{
await foreach (var item in dbSet
.Where(x => EF.Property<string>(x, nameof(BasePerson.Mobile)) != null && EF.Property<string>(x, nameof(BasePerson.Mobile)).Contains(query))
.AsAsyncEnumerable()
.WithCancellation(cancellationToken))
{
yield return item;
}
}
我已经写了很多这样的代码来进行优化。我没有完整的代码可供分析,我建议进行以下更改。
这只是我如何看待代码可能有点问题的示例,但我希望您能理解其中的逻辑
课程
public class Records
{
}
public class QueryResult
{
public QueryResult(string query, List<Records> results)
{
this.QueryToExecute = query;
this.Results = results;
}
public string QueryToExecute { get; set; }
public List<Records> Results { get; set; } = new List<Records>();
public string FailedMessage { get; set; } = string.Empty;
}
功能
public async void Hello()
{
var queryDict = new ConcurrentDictionary<string, List<QueryResult>>();
queryDict.TryAdd("Table1", new List<QueryResult>()
{
new QueryResult("Select XXX FROM Table1 WHERE ID>1 AND ID<50000", new List<Records>()),
new QueryResult("Select XXX FROM Table1 WHERE ID>50001 AND ID<100000", new List<Records>()),
new QueryResult("Select XXX FROM Table1 WHERE ID>100000 AND ID<150000", new List<Records>()),
new QueryResult("Select XXX FROM Table1 WHERE ID>150001 AND ID<200000", new List<Records>()),
});
queryDict.TryAdd("Table2", new List<QueryResult>()
{
new QueryResult("Select XXX FROM Table2 WHERE ID>1 AND ID<50000", new List<Records>()),
new QueryResult("Select XXX FROM Table2 WHERE ID>50001 AND ID<100000", new List<Records>()),
new QueryResult("Select XXX FROM Table2 WHERE ID>100000 AND ID<150000", new List<Records>()),
new QueryResult("Select XXX FROM Table2 WHERE ID>150001 AND ID<200000", new List<Records>()),
});
foreach (var record in queryDict.Values)
{
foreach (var item in record)
{
await Task.WhenAll(
Task.Run(() => RunQuery(item)),
Task.Run(() => RunQuery(item)));
}
}
}
private void RunQuery(QueryResult queryToExecute)
{
// Execute queryusing EF
}