Я пытаюсь использовать TPL, чтобы уменьшить время выполнения приложения. Приложение использует DbContext, и само задание запрашивает базу данных примерно три раза, используя асинхронные методы (FirstOrDefaultAsync
). Я начал получать исключения, такие как:
"System.InvalidOperationException: A second operation started on this context before a previous operation completed. This is usually caused by different threads using the same instance of DbContext, however instance members are not guaranteed to be thread safe. This could also be caused by a nested query being evaluated on the client, if this is the case rewrite the query avoiding nested invocations."
Это привело меня к пониманию, что DbContext
не является безопасным для потоков, и мне нужно решение.
Я пытался создать новый экземпляр базы данных для каждого контекста, но я не думаю, что я делаю это правильно. Ниже я покажу некоторый код для демонстрации моего DbContext
творения, он отличается от того, что я видел в других местах на StackOverflow и других веб-сайтах.
</p>
<pre><code>public static async Task Main()
{
var host = new WebHostBuilder()
.UseEnvironment("Test")
.ConfigureAppConfiguration((builderContext, config) =>
{
var env = builderContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true).AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
}).UseStartup<Startup>();
var testServer = new TestServer(host);
var database = testServer.Host.Services.GetService<CustomDbContext>();
database.Database.EnsureCreated();
var httpClient = testServer.CreateClient();
httpClient.DefaultRequestHeaders.Add(ApiConstants.General.APIKeyHeaderParm, tenant.APISecurityGuid.ToString()); // this works
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json");
var configuration = builder.Build();
var tasks = new List<Task>();
foreach (var obj in database.Objects)
{
tasks.Add(CustomAsyncMethod(obj.Name, database, configuration, httpClient));
}
try
{
Task.WaitAll(tasks.ToArray());
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
Console.ReadKey();
}
//Some other normal code
}
public static async Task CustomAsyncMethod(string name, CustomDbContext database, IConfigurationRoot configuration, HttpClient httpClient)
{
//Lines of normal code stuff
//Inside of nested for loops here
var first = await database.History.FirstOrDefaultAsync(x => x.Id == id);
var second = await database.OtherTable.FirstOrDefaultAsync(y => y.Name == first.Name);
// End nested for loops
// Lines of other normal code stuff
}
В цикле fooreach (foreach (var obj in database.Objects)
) я пытался создать несколько WebHostBuilders
, несколько TestServers
и несколько httpClients
для использования в CustomAsyncMethod
, но все эти попытки, казалось, усугубили ситуацию. В нынешнем виде я получаю InvalidOperationException
несколько раз при каждом запуске программы, и программа прерывается, вероятно, в 70% случаев. 30% времени это закончится успешно.
Как правильно устранить исключения, не выполняя все без TPL?