@@ -73,73 +73,49 @@ public void ResolveProviderDetails(EventRecord eventRecord)
7373 ObjectDisposedException . ThrowIf ( IsDisposed , nameof ( EventProviderDatabaseEventResolver ) ) ;
7474
7575 // Fast path: ConcurrentDictionary is thread-safe for reads, so we can check
76- // without any lock. This avoids serializing all 8 parallel reader threads on
77- // the UpgradeableReadLock for providers that are already cached.
76+ // without any lock. This avoids serializing all parallel reader threads for
77+ // providers that are already cached.
7878 if ( ProviderDetails . ContainsKey ( eventRecord . ProviderName ) ) { return ; }
7979
80- ProviderDetailsLock . EnterUpgradeableReadLock ( ) ;
81-
82- try
80+ using ( ProviderDetailsLock . EnterScope ( ) )
8381 {
8482 // Re-check after acquiring lock - another thread may have added this provider
85- if ( ProviderDetails . ContainsKey ( eventRecord . ProviderName ) )
86- {
87- return ;
88- }
83+ if ( ProviderDetails . ContainsKey ( eventRecord . ProviderName ) ) { return ; }
8984
90- ProviderDetailsLock . EnterWriteLock ( ) ;
91-
92- try
85+ // Use EnterScope() for exception-safe lock management
86+ using ( _databaseAccessLock . EnterScope ( ) )
9387 {
94- // Triple-check after acquiring write lock
95- if ( ProviderDetails . ContainsKey ( eventRecord . ProviderName ) )
96- {
97- return ;
98- }
88+ // Check disposed inside the database lock to prevent race with Dispose().
89+ // This ensures we don't proceed if Dispose() has swapped out _dbContexts.
90+ ObjectDisposedException . ThrowIf ( IsDisposed , nameof ( EventProviderDatabaseEventResolver ) ) ;
9991
100- // Use EnterScope() for exception-safe lock management
101- using ( _databaseAccessLock . EnterScope ( ) )
92+ foreach ( var dbContext in _dbContexts )
10293 {
103- // Check disposed inside the database lock to prevent race with Dispose().
104- // This ensures we don't proceed if Dispose() has swapped out _dbContexts.
105- ObjectDisposedException . ThrowIf ( IsDisposed , nameof ( EventProviderDatabaseEventResolver ) ) ;
106-
107- foreach ( var dbContext in _dbContexts )
108- {
109- // Use EF.Functions.Collate() with NOCASE on both sides for case-insensitive comparison.
110- // Applying to both sides ensures the comparison is case-insensitive regardless of the
111- // case in eventRecord.ProviderName. SQLite will use NOCASE collation for the comparison.
112- // Note: The loop over databases is intentional for priority-based resolution.
113- var details = dbContext . ProviderDetails . FirstOrDefault ( p =>
114- EF . Functions . Collate ( p . ProviderName , "NOCASE" ) ==
115- EF . Functions . Collate ( eventRecord . ProviderName , "NOCASE" ) ) ;
116-
117- if ( details is null ) { continue ; }
118-
119- Logger ? . Debug ( $ "Resolved { eventRecord . ProviderName } provider from database { dbContext . Name } .") ;
120- ProviderDetails . TryAdd ( eventRecord . ProviderName , details ) ;
121-
122- // Exit after first match - databases are sorted by priority (SortDatabases),
123- // so the first database containing the provider is the preferred source.
124- // TryAdd would prevent duplicates anyway, but breaking early avoids unnecessary queries.
125- break ;
126- }
94+ // Use EF.Functions.Collate() with NOCASE on both sides for case-insensitive comparison.
95+ // Applying to both sides ensures the comparison is case-insensitive regardless of the
96+ // case in eventRecord.ProviderName. SQLite will use NOCASE collation for the comparison.
97+ // Note: The loop over databases is intentional for priority-based resolution.
98+ var details = dbContext . ProviderDetails . FirstOrDefault ( p =>
99+ EF . Functions . Collate ( p . ProviderName , "NOCASE" ) ==
100+ EF . Functions . Collate ( eventRecord . ProviderName , "NOCASE" ) ) ;
101+
102+ if ( details is null ) { continue ; }
103+
104+ Logger ? . Debug ( $ "Resolved { eventRecord . ProviderName } provider from database { dbContext . Name } .") ;
105+ ProviderDetails . TryAdd ( eventRecord . ProviderName , details ) ;
106+
107+ // Exit after first match - databases are sorted by priority (SortDatabases),
108+ // so the first database containing the provider is the preferred source.
109+ // TryAdd would prevent duplicates anyway, but breaking early avoids unnecessary queries.
110+ break ;
127111 }
128112 }
129- finally
130- {
131- ProviderDetailsLock . ExitWriteLock ( ) ;
132- }
133113
134114 if ( ! ProviderDetails . ContainsKey ( eventRecord . ProviderName ) )
135115 {
136116 ProviderDetails . TryAdd ( eventRecord . ProviderName , new ProviderDetails { ProviderName = eventRecord . ProviderName } ) ;
137117 }
138118 }
139- finally
140- {
141- ProviderDetailsLock . ExitUpgradeableReadLock ( ) ;
142- }
143119 }
144120
145121 /// <summary>
0 commit comments