Skip to content

Commit 174cff3

Browse files
committed
Fix possible DbContext concurrency issues
Closes #898
1 parent 482b9a2 commit 174cff3

4 files changed

Lines changed: 83 additions & 34 deletions

File tree

OpenBullet2.Web.Tests/Integration/JobIntegrationTests.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,47 @@ public async Task GetMultiRunJob_Admin_Success()
365365
Assert.NotNull(result.Value.ProxyStats);
366366
}
367367

368+
/// <summary>
369+
/// Admin can get the details of a multi run job with multiple group proxy sources.
370+
/// </summary>
371+
[Fact]
372+
public async Task GetMultiRunJob_Admin_MultipleGroupProxySources_Success()
373+
{
374+
// Arrange
375+
using var client = Factory.CreateClient();
376+
var proxyReloadService = GetRequiredService<ProxyReloadService>();
377+
var jobManager = GetRequiredService<JobManagerService>();
378+
var dbContext = GetRequiredService<ApplicationDbContext>();
379+
380+
var groupOne = new ProxyGroupEntity { Name = "Group 1" };
381+
var groupTwo = new ProxyGroupEntity { Name = "Group 2" };
382+
dbContext.ProxyGroups.AddRange(groupOne, groupTwo);
383+
await dbContext.SaveChangesAsync(TestCancellationToken);
384+
385+
var mrJob = CreateMultiRunJob();
386+
mrJob.Name = "Test MRJ";
387+
mrJob.Id = 2;
388+
mrJob.ProxySources =
389+
[
390+
new GroupProxySource(groupOne.Id, proxyReloadService),
391+
new GroupProxySource(groupTwo.Id, proxyReloadService)
392+
];
393+
mrJob.DataPool = new CombinationsDataPool("abc", 3);
394+
jobManager.AddJob(mrJob);
395+
396+
// Act
397+
var queryParams = new
398+
{
399+
id = mrJob.Id
400+
};
401+
var result = await GetJsonAsync<MultiRunJobDto>(
402+
client, "/api/v1/job/multi-run".ToUri(queryParams));
403+
404+
// Assert
405+
Assert.True(result.IsSuccess);
406+
Assert.Equal(["Group 1 (Group)", "Group 2 (Group)"], result.Value.ProxySources);
407+
}
408+
368409
/// <summary>
369410
/// Guest cannot get the details of a multi run job not owned by them.
370411
/// </summary>

OpenBullet2.Web/Controllers/InfoController.cs

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,22 @@ namespace OpenBullet2.Web.Controllers;
2222
[TypeFilter<GuestFilter>]
2323
[ApiVersion("1.0")]
2424
public class InfoController(IAnnouncementService announcementService,
25-
HttpClient httpClient,
26-
IUpdateService updateService, IServiceProvider serviceProvider) : ApiController
25+
HttpClient httpClient, IUpdateService updateService,
26+
JobManagerService jobManager, IProxyRepository proxyRepo,
27+
IWordlistRepository wordlistRepo, IHitRepository hitRepo,
28+
IGuestRepository guestRepo, ConfigService configService,
29+
PluginRepository pluginRepository) : ApiController
2730
{
2831
private readonly IAnnouncementService _announcementService = announcementService;
32+
private readonly ConfigService _configService = configService;
33+
private readonly IGuestRepository _guestRepo = guestRepo;
34+
private readonly IHitRepository _hitRepo = hitRepo;
2935
private readonly HttpClient _httpClient = httpClient;
30-
private readonly IServiceProvider _serviceProvider = serviceProvider;
36+
private readonly JobManagerService _jobManager = jobManager;
37+
private readonly PluginRepository _pluginRepository = pluginRepository;
38+
private readonly IProxyRepository _proxyRepo = proxyRepo;
3139
private readonly IUpdateService _updateService = updateService;
40+
private readonly IWordlistRepository _wordlistRepo = wordlistRepo;
3241

3342
/// <summary>
3443
/// Get information about the server and the environment.
@@ -144,48 +153,38 @@ public async Task<ActionResult<CollectionInfoDto>> GetCollectionInfo(Cancellatio
144153
var apiUser = HttpContext.GetApiUser();
145154

146155
var jobCount = apiUser.Role is UserRole.Admin
147-
? _serviceProvider.GetRequiredService<JobManagerService>()
148-
.Jobs.Count()
149-
: _serviceProvider.GetRequiredService<JobManagerService>()
150-
.Jobs.Count(j => j.OwnerId == apiUser.Id);
156+
? _jobManager.Jobs.Count()
157+
: _jobManager.Jobs.Count(j => j.OwnerId == apiUser.Id);
151158

152159
var proxyCount = apiUser.Role is UserRole.Admin
153-
? await _serviceProvider.GetRequiredService<IProxyRepository>()
154-
.GetAll().CountAsync(cancellationToken)
155-
: await _serviceProvider.GetRequiredService<IProxyRepository>()
156-
.GetAll().CountAsync(p => p.Group != null
160+
? await _proxyRepo.GetAll().CountAsync(cancellationToken)
161+
: await _proxyRepo.GetAll().CountAsync(p => p.Group != null
157162
&& p.Group.Owner != null
158163
&& p.Group.Owner.Id == apiUser.Id, cancellationToken);
159164

160165
var wordlistCount = apiUser.Role is UserRole.Admin
161-
? await _serviceProvider.GetRequiredService<IWordlistRepository>()
162-
.GetAll().CountAsync(cancellationToken)
163-
: await _serviceProvider.GetRequiredService<IWordlistRepository>()
164-
.GetAll().CountAsync(w => w.Owner != null && w.Owner.Id == apiUser.Id, cancellationToken);
166+
? await _wordlistRepo.GetAll().CountAsync(cancellationToken)
167+
: await _wordlistRepo.GetAll()
168+
.CountAsync(w => w.Owner != null && w.Owner.Id == apiUser.Id, cancellationToken);
165169

166170
var wordlistLines = apiUser.Role is UserRole.Admin
167-
? await _serviceProvider.GetRequiredService<IWordlistRepository>()
168-
.GetAll().SumAsync(w => (long)w.Total, cancellationToken)
169-
: await _serviceProvider.GetRequiredService<IWordlistRepository>()
170-
.GetAll()
171+
? await _wordlistRepo.GetAll().SumAsync(w => (long)w.Total, cancellationToken)
172+
: await _wordlistRepo.GetAll()
171173
.Where(w => w.Owner != null && w.Owner.Id == apiUser.Id)
172174
.SumAsync(w => (long)w.Total, cancellationToken);
173175

174176
var hitCount = apiUser.Role is UserRole.Admin
175-
? await _serviceProvider.GetRequiredService<IHitRepository>()
176-
.GetAll().CountAsync(cancellationToken)
177-
: await _serviceProvider.GetRequiredService<IHitRepository>()
178-
.GetAll().CountAsync(h => h.OwnerId == apiUser.Id, cancellationToken);
177+
? await _hitRepo.GetAll().CountAsync(cancellationToken)
178+
: await _hitRepo.GetAll().CountAsync(h => h.OwnerId == apiUser.Id, cancellationToken);
179179

180-
var configCount = _serviceProvider
181-
.GetRequiredService<ConfigService>().Configs.Count;
180+
var configCount = _configService.Configs.Count;
182181

183182
var guestCount = apiUser.Role is UserRole.Admin
184-
? await _serviceProvider.GetRequiredService<IGuestRepository>().GetAll().CountAsync(cancellationToken)
183+
? await _guestRepo.GetAll().CountAsync(cancellationToken)
185184
: 1; // The guest shouldn't see the total number of guests
186185

187186
var pluginCount = apiUser.Role is UserRole.Admin
188-
? _serviceProvider.GetRequiredService<PluginRepository>().GetPlugins().Count()
187+
? _pluginRepository.GetPlugins().Count()
189188
: 0; // The guest shouldn't see the total number of plugins
190189

191190
return new CollectionInfoDto

OpenBullet2.Web/Controllers/JobController.cs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -924,13 +924,24 @@ private async Task<MultiRunJobDto> MapMultiRunJobDto(MultiRunJob job, Cancellati
924924
_ => throw new NotImplementedException()
925925
};
926926

927-
var proxySources = await Task.WhenAll(job.ProxySources.Select(async s => s switch
927+
var groupIds = job.ProxySources
928+
.OfType<GroupProxySource>()
929+
.Select(s => s.GroupId)
930+
.Where(id => id != -1)
931+
.Distinct()
932+
.ToList();
933+
934+
var groupNames = await _proxyGroupRepo.GetAll()
935+
.Where(g => groupIds.Contains(g.Id))
936+
.ToDictionaryAsync(g => g.Id, g => g.Name ?? "Invalid", cancellationToken);
937+
938+
var proxySources = job.ProxySources.Select(s => s switch
928939
{
929-
GroupProxySource g => $"{await GetProxyGroupName(g.GroupId, cancellationToken)} (Group)",
940+
GroupProxySource g => $"{GetProxyGroupName(g.GroupId, groupNames)} (Group)",
930941
FileProxySource f => $"{f.FileName} (File)",
931942
RemoteProxySource r => $"{r.Url} (Remote)",
932943
_ => throw new NotImplementedException()
933-
}));
944+
}).ToArray();
934945

935946
var hitOutputs = job.HitOutputs.Select(o => o switch
936947
{
@@ -1028,15 +1039,14 @@ job.Config is not null
10281039
};
10291040
}
10301041

1031-
private async Task<string> GetProxyGroupName(int id, CancellationToken cancellationToken)
1042+
private static string GetProxyGroupName(int id, IReadOnlyDictionary<int, string> groupNames)
10321043
{
10331044
if (id == -1)
10341045
{
10351046
return "All";
10361047
}
10371048

1038-
var proxyGroup = await _proxyGroupRepo.GetAsync(id, cancellationToken);
1039-
return proxyGroup?.Name ?? "Invalid";
1049+
return groupNames.GetValueOrDefault(id, "Invalid");
10401050
}
10411051

10421052
private static JobOptionsWrapper DeserializeJobOptions(JobEntity entity)

OpenBullet2.Web/Program.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,6 @@
125125
builder.Services.AddScoped<ProxySourceFactoryService>();
126126

127127
// Singleton
128-
builder.Services.AddSingleton(sp => sp); // The service provider itself
129128
builder.Services.AddSingleton<IAuthTokenService, AuthTokenService>();
130129
builder.Services.AddSingleton<IAnnouncementService, AnnouncementService>();
131130
builder.Services.AddSingleton<IUpdateService, UpdateService>();

0 commit comments

Comments
 (0)