1- using System ;
2- using System . Collections . Generic ;
31using System . Collections . ObjectModel ;
42using System . ComponentModel ;
53using System . ComponentModel . Composition ;
6- using System . IO ;
7- using System . Linq ;
84using System . Reflection ;
9- using JetBrains . Annotations ;
105#if NETCOREAPP
116using System . Runtime . Loader ;
127#endif
@@ -32,6 +27,7 @@ public class ExportComponentManager
3227
3328 private readonly object _locker = new ( ) ;
3429 private readonly HashSet < Assembly > _loadedAssemblies = new ( ) ;
30+ private readonly List < ( Assembly Assembly , List < IExportData > Removed ) > _removedExports = new ( ) ;
3531 private readonly Dictionary < Uri , IExportData > _exports = new ( ) ;
3632 private readonly MultiDoubleDictionary < Type , string , List < IExportData > , IExportData > _exportTree = new ( ) ;
3733
@@ -102,6 +98,9 @@ public ExportComponent GetExport(Uri location)
10298 if ( data == null )
10399 return null ;
104100
101+ while ( data . OverrideBy != null )
102+ data = data . OverrideBy ;
103+
105104 return data . Component ;
106105 }
107106 }
@@ -322,6 +321,7 @@ public void LoadAssembly(Type typeInRequestedAssembly)
322321 public void LoadAssembly ( Assembly assembly )
323322 {
324323 var changedContractTypes = new HashSet < Type > ( ) ;
324+ var duplicateUriHandling = assembly . GetCustomAttribute < AssemblyExportSettingsAttribute > ( ) ? . DuplicateUriHandling ?? ExportDuplicateUriHandling . SkipCurrent ;
325325
326326 lock ( _locker )
327327 {
@@ -390,19 +390,45 @@ public void LoadAssembly(Assembly assembly)
390390 // Apply changes
391391 lock ( _locker )
392392 {
393+ List < IExportData > removed = null ;
394+
393395 foreach ( var definition in items )
394396 {
395- if ( _exports . ContainsKey ( definition . Location ) )
397+ if ( _exports . TryGetValue ( definition . Location , out var existing ) && duplicateUriHandling == ExportDuplicateUriHandling . SkipCurrent )
396398 continue ;
397399
398400 var data = new ExportData ( definition ) ;
399401
400- _exports . Add ( definition . Location , data ) ;
402+ if ( existing == null )
403+ _exports . Add ( definition . Location , data ) ;
404+ else if ( duplicateUriHandling == ExportDuplicateUriHandling . OverrideExisting )
405+ {
406+ while ( existing . OverrideBy != null )
407+ existing = existing . OverrideBy ;
408+
409+ existing . OverrideBy = data ;
410+ }
411+ else
412+ {
413+ removed ??= new List < IExportData > ( 2 ) ;
414+ removed . Add ( existing ) ;
415+
416+ UnregisterCore ( existing . Component , true ) ;
417+
418+ while ( _exports . TryGetValue ( definition . Location , out existing ) )
419+ UnregisterCore ( existing . Component , true ) ;
420+
421+ _exports . Add ( definition . Location , data ) ;
422+ }
423+
401424 _exportTree . Add ( definition . ContractType , definition . ContractName ?? string . Empty , data ) ;
402425
403426 changedContractTypes . Add ( definition . ContractType ) ;
404427 }
405428
429+ if ( removed != null )
430+ _removedExports . Add ( ( assembly , removed ) ) ;
431+
406432 if ( loadDirectly )
407433 _loadedAssemblyOutsideOfCache = true ;
408434 }
@@ -428,19 +454,56 @@ public void UnloadAssembly(Assembly assembly)
428454 foreach ( var collection in _exportTree . Values )
429455 {
430456 for ( var i = collection . Count - 1 ; i >= 0 ; i -- )
431- if ( collection [ i ] . OriginAssembly == assembly )
457+ {
458+ var item = collection [ i ] ;
459+ if ( item . OriginAssembly == assembly )
432460 {
433- changedContractTypes . Add ( collection [ i ] . Definition . ContractType ) ;
434- _exports . Remove ( collection [ i ] . Definition . Location ) ;
461+ changedContractTypes . Add ( item . Definition . ContractType ) ;
435462 collection . RemoveAt ( i ) ;
463+
464+ RemoveLocation ( item ) ;
436465 }
466+ }
437467 }
468+
469+ var removed = _removedExports . FirstOrDefault ( x => x . Assembly == assembly ) . Removed ;
470+ if ( removed != null )
471+ foreach ( var data in removed )
472+ {
473+ if ( _loadedAssemblies . Contains ( data . OriginAssembly ) )
474+ {
475+ Register ( data . Component , true , true ) ;
476+ changedContractTypes . Add ( data . Definition . ContractType ) ;
477+ }
478+ }
438479 }
439480
440481 RaiseExportCollectionChanged ( changedContractTypes . ToArray ( ) ) ;
441482 }
483+ private void RemoveLocation ( IExportData item )
484+ {
485+ var location = item . Definition . Location ;
486+ if ( _exports . TryGetValue ( location , out var data ) )
487+ if ( data == item )
488+ {
489+ if ( data . OverrideBy != null )
490+ _exports [ location ] = data . OverrideBy ;
491+ else
492+ _exports . Remove ( location ) ;
493+ }
494+ else
495+ {
496+ for ( ; data . OverrideBy != null ; data = data . OverrideBy )
497+ if ( data . OverrideBy == item )
498+ {
499+ data . OverrideBy = data . OverrideBy . OverrideBy ;
500+ break ;
501+ }
502+ }
503+ }
442504
443- public void Register < T > ( ExportComponent < T > component )
505+ public void Register < T > ( ExportComponent < T > component ) => Register ( ( ExportComponent ) component ) ;
506+ private void Register ( ExportComponent component , bool skipExisting = false , bool skipEvent = false )
444507 {
445508 if ( component == null )
446509 throw new ArgumentNullException ( nameof ( component ) ) ;
@@ -451,13 +514,19 @@ public void Register<T>(ExportComponent<T> component)
451514 {
452515 var data = new RegisteredExportData ( component ) ;
453516 if ( _exports . ContainsKey ( component . Location ) )
517+ {
518+ if ( skipExisting )
519+ return ;
520+
454521 throw new Exception ( string . Format ( "Component with the same url ({0}) is already registered." , component . Location ) ) ;
522+ }
455523
456524 _exports . Add ( component . Location , data ) ;
457525 _exportTree . Add ( component . ContractType , component . ContractName ?? string . Empty , data ) ;
458526 }
459527
460- RaiseExportCollectionChanged ( new [ ] { component . ContractType } ) ;
528+ if ( skipEvent )
529+ RaiseExportCollectionChanged ( new [ ] { component . ContractType } ) ;
461530 }
462531 public bool Unregister ( Uri location )
463532 {
@@ -482,7 +551,7 @@ public bool Unregister(Uri location)
482551 }
483552 }
484553
485- _exports . Remove ( location ) ;
554+ RemoveLocation ( data ) ;
486555 contractType = data . Definition . ContractType ;
487556 }
488557 }
@@ -495,7 +564,8 @@ public bool Unregister(Uri location)
495564
496565 return false ;
497566 }
498- public bool Unregister ( ExportComponent component )
567+ public bool Unregister ( ExportComponent component ) => UnregisterCore ( component , false ) ;
568+ public bool UnregisterCore ( ExportComponent component , bool force )
499569 {
500570 if ( component == null )
501571 throw new ArgumentNullException ( nameof ( component ) ) ;
@@ -506,9 +576,9 @@ public bool Unregister(ExportComponent component)
506576 if ( collection != null )
507577 {
508578 for ( var i = collection . Count - 1 ; i >= 0 ; i -- )
509- if ( collection [ i ] . IsRegistered && collection [ i ] . Component == component )
579+ if ( collection [ i ] . Component == component && ( force || collection [ i ] . IsRegistered ) )
510580 {
511- _exports . Remove ( collection [ i ] . Definition . Location ) ;
581+ RemoveLocation ( collection [ i ] ) ;
512582 collection . RemoveAt ( i ) ;
513583 return true ;
514584 }
@@ -736,6 +806,8 @@ private interface IExportData
736806 ExportComponent Component { get ; }
737807 Assembly OriginAssembly { get ; }
738808 bool IsRegistered { get ; }
809+
810+ IExportData OverrideBy { get ; set ; }
739811 }
740812 private class ExportData : IExportData
741813 {
@@ -758,6 +830,7 @@ public ExportComponent Component
758830 }
759831 public Assembly OriginAssembly => Definition . ValueTypeAssembly ;
760832 public bool IsRegistered => false ;
833+ public IExportData OverrideBy { get ; set ; }
761834
762835 public ExportData ( ExportComponentDefinition definition )
763836 {
@@ -770,6 +843,7 @@ private class RegisteredExportData : IExportData
770843 public ExportComponent Component { get ; }
771844 public Assembly OriginAssembly => Component . OriginAssembly ;
772845 public bool IsRegistered => true ;
846+ public IExportData OverrideBy { get ; set ; }
773847
774848 public RegisteredExportData ( ExportComponent component )
775849 {
0 commit comments