1
1
using System ;
2
2
using System . Collections . Concurrent ;
3
+ using System . Diagnostics . CodeAnalysis ;
3
4
using System . IO ;
4
5
using System . Linq ;
5
6
using System . Reflection ;
7
+ using Stef . Validation ;
6
8
7
9
namespace WireMock . Plugin ;
8
10
9
11
internal static class PluginLoader
10
12
{
11
- private static readonly ConcurrentDictionary < Type , Type > Assemblies = new ( ) ;
13
+ private static readonly ConcurrentDictionary < string , Type > Assemblies = new ( ) ;
12
14
13
- public static T Load < T > ( params object [ ] args ) where T : class
15
+ public static TInterface Load < TInterface > ( params object [ ] args ) where TInterface : class
14
16
{
15
- var foundType = Assemblies . GetOrAdd ( typeof ( T ) , ( type ) =>
17
+ var key = typeof ( TInterface ) . FullName ! ;
18
+
19
+ var pluginType = Assemblies . GetOrAdd ( key , _ =>
16
20
{
17
- var files = Directory . GetFiles ( Directory . GetCurrentDirectory ( ) , "*.dll" ) ;
21
+ if ( TryFindTypeInDlls < TInterface > ( null , out var foundType ) )
22
+ {
23
+ return foundType ;
24
+ }
25
+
26
+ throw new DllNotFoundException ( $ "No dll found which implements Interface '{ key } '.") ;
27
+ } ) ;
28
+
29
+ return ( TInterface ) Activator . CreateInstance ( pluginType , args ) ! ;
30
+ }
31
+
32
+ public static TInterface LoadByFullName < TInterface > ( string implementationTypeFullName , params object [ ] args ) where TInterface : class
33
+ {
34
+ Guard . NotNullOrEmpty ( implementationTypeFullName ) ;
35
+
36
+ var @interface = typeof ( TInterface ) . FullName ;
37
+ var key = $ "{ @interface } _{ implementationTypeFullName } ";
38
+
39
+ var pluginType = Assemblies . GetOrAdd ( key , _ =>
40
+ {
41
+ if ( TryFindTypeInDlls < TInterface > ( implementationTypeFullName , out var foundType ) )
42
+ {
43
+ return foundType ;
44
+ }
45
+
46
+ throw new DllNotFoundException ( $ "No dll found which implements Interface '{ @interface } ' and has FullName '{ implementationTypeFullName } '.") ;
47
+ } ) ;
18
48
19
- Type ? pluginType = null ;
20
- foreach ( var file in files )
49
+ return ( TInterface ) Activator . CreateInstance ( pluginType , args ) ! ;
50
+ }
51
+
52
+ private static bool TryFindTypeInDlls < TInterface > ( string ? implementationTypeFullName , [ NotNullWhen ( true ) ] out Type ? pluginType ) where TInterface : class
53
+ {
54
+ foreach ( var file in Directory . GetFiles ( Directory . GetCurrentDirectory ( ) , "*.dll" ) )
55
+ {
56
+ try
21
57
{
22
- try
58
+ var assembly = Assembly . Load ( new AssemblyName
23
59
{
24
- var assembly = Assembly . Load ( new AssemblyName
25
- {
26
- Name = Path . GetFileNameWithoutExtension ( file )
27
- } ) ;
28
-
29
- pluginType = GetImplementationTypeByInterface < T > ( assembly ) ;
30
- if ( pluginType != null )
31
- {
32
- break ;
33
- }
34
- }
35
- catch
60
+ Name = Path . GetFileNameWithoutExtension ( file )
61
+ } ) ;
62
+
63
+ if ( TryGetImplementationTypeByInterfaceAndOptionalFullName < TInterface > ( assembly , implementationTypeFullName , out pluginType ) )
36
64
{
37
- // no-op: just try next .dll
65
+ return true ;
38
66
}
39
67
}
40
-
41
- if ( pluginType != null )
68
+ catch
42
69
{
43
- return pluginType ;
70
+ // no-op: just try next .dll
44
71
}
72
+ }
45
73
46
- throw new DllNotFoundException ( $ "No dll found which implements type '{ type } '") ;
47
- } ) ;
48
-
49
- return ( T ) Activator . CreateInstance ( foundType , args ) ;
74
+ pluginType = null ;
75
+ return false ;
50
76
}
51
77
52
- private static Type ? GetImplementationTypeByInterface < T > ( Assembly assembly )
78
+ private static bool TryGetImplementationTypeByInterfaceAndOptionalFullName < T > ( Assembly assembly , string ? implementationTypeFullName , [ NotNullWhen ( true ) ] out Type ? type )
53
79
{
54
- return assembly . GetTypes ( ) . FirstOrDefault ( t => typeof ( T ) . IsAssignableFrom ( t ) && ! t . GetTypeInfo ( ) . IsInterface ) ;
80
+ type = assembly
81
+ . GetTypes ( )
82
+ . FirstOrDefault ( t =>
83
+ typeof ( T ) . IsAssignableFrom ( t ) && ! t . GetTypeInfo ( ) . IsInterface &&
84
+ implementationTypeFullName == null || string . Equals ( t . FullName , implementationTypeFullName , StringComparison . OrdinalIgnoreCase )
85
+ ) ;
86
+
87
+ return type != null ;
55
88
}
56
89
}
0 commit comments