|
6 | 6 | using EventLogExpert.Eventing.Providers; |
7 | 7 | using Microsoft.Extensions.DependencyInjection; |
8 | 8 | using System.CommandLine; |
| 9 | +using System.Text.RegularExpressions; |
9 | 10 |
|
10 | 11 | namespace EventLogExpert.EventDbTool; |
11 | 12 |
|
@@ -83,50 +84,78 @@ private void CreateDatabase(string path, string? source, string? filter, string? |
83 | 84 | return; |
84 | 85 | } |
85 | 86 |
|
86 | | - if (source is not null && !ProviderSource.TryValidate(source, Logger)) { return; } |
| 87 | + if (!RegexHelper.TryCreate(filter, Logger, out _)) { return; } |
87 | 88 |
|
88 | | - HashSet<string> skipProviderNames = new(StringComparer.OrdinalIgnoreCase); |
| 89 | + if (source is not null && !ProviderSource.TryValidate(source, Logger)) { return; } |
89 | 90 |
|
90 | | - if (!string.IsNullOrWhiteSpace(skipProvidersInFile)) |
| 91 | + try |
91 | 92 | { |
92 | | - if (!ProviderSource.TryValidate(skipProvidersInFile, Logger)) { return; } |
| 93 | + HashSet<string> skipProviderNames = new(StringComparer.OrdinalIgnoreCase); |
93 | 94 |
|
94 | | - foreach (var name in ProviderSource.LoadProviderNames(skipProvidersInFile, Logger)) |
| 95 | + if (!string.IsNullOrWhiteSpace(skipProvidersInFile)) |
95 | 96 | { |
96 | | - skipProviderNames.Add(name); |
| 97 | + if (!ProviderSource.TryValidate(skipProvidersInFile, Logger)) { return; } |
| 98 | + |
| 99 | + foreach (var name in ProviderSource.LoadProviderNames(skipProvidersInFile, Logger)) |
| 100 | + { |
| 101 | + skipProviderNames.Add(name); |
| 102 | + } |
| 103 | + |
| 104 | + Logger.Info($"Found {skipProviderNames.Count} providers in {skipProvidersInFile}. These will not be included in the new database."); |
97 | 105 | } |
98 | 106 |
|
99 | | - Logger.Info($"Found {skipProviderNames.Count} providers in {skipProvidersInFile}. These will not be included in the new database."); |
100 | | - } |
| 107 | + // Load provider names first (cheap string-only query) for the empty check and header |
| 108 | + // formatting. This avoids materializing all ProviderDetails (with large compressed |
| 109 | + // payloads) just to compute the column widths. |
| 110 | + IReadOnlyList<string> providerNames = source is null |
| 111 | + ? GetLocalProviderNames(filter, Logger) |
| 112 | + .Where(n => !skipProviderNames.Contains(n)).ToList() |
| 113 | + : ProviderSource.LoadProviderNames(source, Logger, filter) |
| 114 | + .Where(n => !skipProviderNames.Contains(n)).ToList(); |
101 | 115 |
|
102 | | - IEnumerable<ProviderDetails> providersToAdd = source is null |
103 | | - ? LoadLocalProviders(filter, skipProviderNames) |
104 | | - : ProviderSource.LoadProviders(source, Logger, filter, skipProviderNames); |
| 116 | + if (providerNames.Count == 0) |
| 117 | + { |
| 118 | + Logger.Warn($"No providers to add to the new database."); |
| 119 | + return; |
| 120 | + } |
105 | 121 |
|
106 | | - var providersNotSkipped = providersToAdd.ToList(); |
| 122 | + using var dbContext = new EventProviderDbContext(path, false, Logger); |
107 | 123 |
|
108 | | - if (providersNotSkipped.Count == 0) |
109 | | - { |
110 | | - Logger.Warn($"No providers to add to the new database."); |
111 | | - return; |
112 | | - } |
| 124 | + LogProviderDetailHeader(providerNames); |
113 | 125 |
|
114 | | - using var dbContext = new EventProviderDbContext(path, false, Logger); |
| 126 | + // Stream details directly into the DbContext. Batch saves prevent the change tracker |
| 127 | + // from accumulating all entities in memory at once. |
| 128 | + const int batchSize = 100; |
| 129 | + var count = 0; |
115 | 130 |
|
116 | | - LogProviderDetailHeader(providersNotSkipped.Select(p => p.ProviderName)); |
| 131 | + IEnumerable<ProviderDetails> providersToAdd = source is null |
| 132 | + ? LoadLocalProviders(filter, skipProviderNames) |
| 133 | + : ProviderSource.LoadProviders(source, Logger, filter, skipProviderNames); |
117 | 134 |
|
118 | | - foreach (var details in providersNotSkipped) |
119 | | - { |
120 | | - dbContext.ProviderDetails.Add(details); |
121 | | - LogProviderDetails(details); |
122 | | - } |
| 135 | + foreach (var details in providersToAdd) |
| 136 | + { |
| 137 | + dbContext.ProviderDetails.Add(details); |
| 138 | + LogProviderDetails(details); |
| 139 | + count++; |
| 140 | + |
| 141 | + if (count % batchSize == 0) |
| 142 | + { |
| 143 | + dbContext.SaveChanges(); |
| 144 | + dbContext.ChangeTracker.Clear(); |
| 145 | + } |
| 146 | + } |
123 | 147 |
|
124 | | - Logger.Info($""); |
125 | | - Logger.Info($"Saving database. Please wait..."); |
| 148 | + Logger.Info($""); |
| 149 | + Logger.Info($"Saving database. Please wait..."); |
126 | 150 |
|
127 | | - dbContext.SaveChanges(); |
| 151 | + dbContext.SaveChanges(); |
128 | 152 |
|
129 | | - Logger.Info($"Done!"); |
| 153 | + Logger.Info($"Done!"); |
| 154 | + } |
| 155 | + catch (RegexMatchTimeoutException) |
| 156 | + { |
| 157 | + Logger.Error($"The --filter regex timed out. The pattern may cause catastrophic backtracking."); |
| 158 | + } |
130 | 159 | } |
131 | 160 |
|
132 | 161 | } |
0 commit comments