@@ -266,11 +266,14 @@ static SqlMapper()
266
266
[ MemberNotNull ( nameof ( typeHandlers ) ) ]
267
267
private static void ResetTypeHandlers ( bool clone )
268
268
{
269
- typeHandlers = [ ] ;
270
- AddTypeHandlerImpl ( typeof ( DataTable ) , new DataTableHandler ( ) , clone ) ;
271
- AddTypeHandlerImpl ( typeof ( XmlDocument ) , new XmlDocumentHandler ( ) , clone ) ;
272
- AddTypeHandlerImpl ( typeof ( XDocument ) , new XDocumentHandler ( ) , clone ) ;
273
- AddTypeHandlerImpl ( typeof ( XElement ) , new XElementHandler ( ) , clone ) ;
269
+ lock ( typeHandlersSyncLock )
270
+ {
271
+ typeHandlers = [ ] ;
272
+ AddTypeHandlerCore ( typeof ( DataTable ) , new DataTableHandler ( ) , clone ) ;
273
+ AddTypeHandlerCore ( typeof ( XmlDocument ) , new XmlDocumentHandler ( ) , clone ) ;
274
+ AddTypeHandlerCore ( typeof ( XDocument ) , new XDocumentHandler ( ) , clone ) ;
275
+ AddTypeHandlerCore ( typeof ( XElement ) , new XElementHandler ( ) , clone ) ;
276
+ }
274
277
}
275
278
276
279
/// <summary>
@@ -339,7 +342,7 @@ public static void RemoveTypeMap(Type type)
339
342
/// </summary>
340
343
/// <param name="type">The type to handle.</param>
341
344
/// <param name="handler">The handler to process the <paramref name="type"/>.</param>
342
- public static void AddTypeHandler ( Type type , ITypeHandler handler ) => AddTypeHandlerImpl ( type , handler , true ) ;
345
+ public static void AddTypeHandler ( Type type , ITypeHandler handler ) => AddTypeHandlerCore ( type , handler , true ) ;
343
346
/// <summary>
344
347
/// Determine if the specified type will be processed by a custom handler.
345
348
/// </summary>
@@ -353,7 +356,16 @@ public static void RemoveTypeMap(Type type)
353
356
/// <param name="type">The type to handle.</param>
354
357
/// <param name="handler">The handler to process the <paramref name="type"/>.</param>
355
358
/// <param name="clone">Whether to clone the current type handler map.</param>
359
+ [ Obsolete ( "Please use " + nameof ( AddTypeHandler ) , error : true ) ]
360
+ [ Browsable ( false ) , EditorBrowsable ( EditorBrowsableState . Never ) ]
356
361
public static void AddTypeHandlerImpl ( Type type , ITypeHandler ? handler , bool clone )
362
+ {
363
+ // this method was accidentally made public; we'll mark it as illegal, but
364
+ // preserve existing usage in compiled code; sorry about this!
365
+ AddTypeHandlerCore ( type , handler , true ) ; // do not allow suppress clone
366
+ }
367
+
368
+ private static void AddTypeHandlerCore ( Type type , ITypeHandler ? handler , bool clone )
357
369
{
358
370
if ( type is null ) throw new ArgumentNullException ( nameof ( type ) ) ;
359
371
@@ -373,39 +385,45 @@ public static void AddTypeHandlerImpl(Type type, ITypeHandler? handler, bool clo
373
385
}
374
386
}
375
387
376
- var snapshot = typeHandlers ;
377
- if ( snapshot . TryGetValue ( type , out var oldValue ) && handler == oldValue ) return ; // nothing to do
388
+ // synchronize between callers mutating type-handlers; note that regular query
389
+ // code may still be accessing the field, so we still use snapshot/mutate/swap;
390
+ // the synchronize is just to prevent lost writes
391
+ lock ( typeHandlersSyncLock )
392
+ {
393
+ if ( typeHandlers . TryGetValue ( type , out var oldValue ) && handler == oldValue ) return ; // nothing to do
378
394
379
- var newCopy = clone ? new Dictionary < Type , ITypeHandler > ( snapshot ) : snapshot ;
395
+ var newCopy = clone ? new Dictionary < Type , ITypeHandler > ( typeHandlers ) : typeHandlers ;
380
396
381
397
#pragma warning disable 618
382
- typeof ( TypeHandlerCache < > ) . MakeGenericType ( type ) . GetMethod ( nameof ( TypeHandlerCache < int > . SetHandler ) , BindingFlags . Static | BindingFlags . NonPublic ) ! . Invoke ( null , [ handler ] ) ;
383
- if ( secondary is not null )
384
- {
385
- typeof ( TypeHandlerCache < > ) . MakeGenericType ( secondary ) . GetMethod ( nameof ( TypeHandlerCache < int > . SetHandler ) , BindingFlags . Static | BindingFlags . NonPublic ) ! . Invoke ( null , [ handler ] ) ;
386
- }
398
+ typeof ( TypeHandlerCache < > ) . MakeGenericType ( type ) . GetMethod ( nameof ( TypeHandlerCache < int > . SetHandler ) , BindingFlags . Static | BindingFlags . NonPublic ) ! . Invoke ( null , [ handler ] ) ;
399
+ if ( secondary is not null )
400
+ {
401
+ typeof ( TypeHandlerCache < > ) . MakeGenericType ( secondary ) . GetMethod ( nameof ( TypeHandlerCache < int > . SetHandler ) , BindingFlags . Static | BindingFlags . NonPublic ) ! . Invoke ( null , [ handler ] ) ;
402
+ }
387
403
#pragma warning restore 618
388
- if ( handler is null )
389
- {
390
- newCopy . Remove ( type ) ;
391
- if ( secondary is not null ) newCopy . Remove ( secondary ) ;
392
- }
393
- else
394
- {
395
- newCopy [ type ] = handler ;
396
- if ( secondary is not null ) newCopy [ secondary ] = handler ;
404
+ if ( handler is null )
405
+ {
406
+ newCopy . Remove ( type ) ;
407
+ if ( secondary is not null ) newCopy . Remove ( secondary ) ;
408
+ }
409
+ else
410
+ {
411
+ newCopy [ type ] = handler ;
412
+ if ( secondary is not null ) newCopy [ secondary ] = handler ;
413
+ }
414
+ typeHandlers = newCopy ;
397
415
}
398
- typeHandlers = newCopy ;
399
416
}
400
417
401
418
/// <summary>
402
419
/// Configure the specified type to be processed by a custom handler.
403
420
/// </summary>
404
421
/// <typeparam name="T">The type to handle.</typeparam>
405
422
/// <param name="handler">The handler for the type <typeparamref name="T"/>.</param>
406
- public static void AddTypeHandler < T > ( TypeHandler < T > handler ) => AddTypeHandlerImpl ( typeof ( T ) , handler , true ) ;
423
+ public static void AddTypeHandler < T > ( TypeHandler < T > handler ) => AddTypeHandlerCore ( typeof ( T ) , handler , true ) ;
407
424
408
425
private static Dictionary < Type , ITypeHandler > typeHandlers ;
426
+ private static readonly object typeHandlersSyncLock = new ( ) ;
409
427
410
428
internal const string LinqBinary = "System.Data.Linq.Binary" ;
411
429
@@ -479,7 +497,7 @@ public static void SetDbType(IDataParameter parameter, object value)
479
497
{
480
498
handler = ( ITypeHandler ) Activator . CreateInstance (
481
499
typeof ( SqlDataRecordHandler < > ) . MakeGenericType ( argTypes ) ) ! ;
482
- AddTypeHandlerImpl ( type , handler , true ) ;
500
+ AddTypeHandlerCore ( type , handler , true ) ;
483
501
return DbType . Object ;
484
502
}
485
503
catch
0 commit comments