@@ -266,11 +266,14 @@ static SqlMapper()
266266 [ MemberNotNull ( nameof ( typeHandlers ) ) ]
267267 private static void ResetTypeHandlers ( bool clone )
268268 {
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+ }
274277 }
275278
276279 /// <summary>
@@ -339,7 +342,7 @@ public static void RemoveTypeMap(Type type)
339342 /// </summary>
340343 /// <param name="type">The type to handle.</param>
341344 /// <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 ) ;
343346 /// <summary>
344347 /// Determine if the specified type will be processed by a custom handler.
345348 /// </summary>
@@ -353,7 +356,16 @@ public static void RemoveTypeMap(Type type)
353356 /// <param name="type">The type to handle.</param>
354357 /// <param name="handler">The handler to process the <paramref name="type"/>.</param>
355358 /// <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 ) ]
356361 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 )
357369 {
358370 if ( type is null ) throw new ArgumentNullException ( nameof ( type ) ) ;
359371
@@ -373,39 +385,45 @@ public static void AddTypeHandlerImpl(Type type, ITypeHandler? handler, bool clo
373385 }
374386 }
375387
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
378394
379- var newCopy = clone ? new Dictionary < Type , ITypeHandler > ( snapshot ) : snapshot ;
395+ var newCopy = clone ? new Dictionary < Type , ITypeHandler > ( typeHandlers ) : typeHandlers ;
380396
381397#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+ }
387403#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 ;
397415 }
398- typeHandlers = newCopy ;
399416 }
400417
401418 /// <summary>
402419 /// Configure the specified type to be processed by a custom handler.
403420 /// </summary>
404421 /// <typeparam name="T">The type to handle.</typeparam>
405422 /// <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 ) ;
407424
408425 private static Dictionary < Type , ITypeHandler > typeHandlers ;
426+ private static readonly object typeHandlersSyncLock = new ( ) ;
409427
410428 internal const string LinqBinary = "System.Data.Linq.Binary" ;
411429
@@ -479,7 +497,7 @@ public static void SetDbType(IDataParameter parameter, object value)
479497 {
480498 handler = ( ITypeHandler ) Activator . CreateInstance (
481499 typeof ( SqlDataRecordHandler < > ) . MakeGenericType ( argTypes ) ) ! ;
482- AddTypeHandlerImpl ( type , handler , true ) ;
500+ AddTypeHandlerCore ( type , handler , true ) ;
483501 return DbType . Object ;
484502 }
485503 catch
0 commit comments