@@ -14,6 +14,11 @@ internal class ServiceMatch
14
14
{
15
15
private readonly ICollection < ServiceDescriptor > _requiredDescriptors = new Collection < ServiceDescriptor > ( ) ;
16
16
private readonly ICollection < ServiceDescriptor > _optionalDescriptors = new Collection < ServiceDescriptor > ( ) ;
17
+
18
+ // We may not be able to load the type at all -- for example third-party services that we want to allow.
19
+ // For this, track the types as strings and we'll match them during IsMatch. These will always be optional.
20
+ private readonly ICollection < ExternalService > _optionalExternalServices = new Collection < ExternalService > ( ) ;
21
+
17
22
private readonly MatchType _match ;
18
23
19
24
private ServiceMatch ( Type serviceType , MatchType match )
@@ -136,6 +141,16 @@ public void AddOptional<TImplementation>(ServiceLifetime lifetime)
136
141
_optionalDescriptors . Add ( desc ) ;
137
142
}
138
143
144
+ public void AddOptionalExternal ( string externalTypeName , string assemblyName , string assemblyPublicKey )
145
+ {
146
+ if ( _match != MatchType . Collection )
147
+ {
148
+ throw new InvalidOperationException ( "Optional matches only apply to Collections." ) ;
149
+ }
150
+
151
+ _optionalExternalServices . Add ( new ExternalService ( externalTypeName , assemblyName , assemblyPublicKey ) ) ;
152
+ }
153
+
139
154
public IEnumerable < InvalidServiceDescriptor > FindInvalidServices ( IServiceCollection services )
140
155
{
141
156
if ( services == null )
@@ -163,7 +178,7 @@ public IEnumerable<InvalidServiceDescriptor> FindInvalidServices(IServiceCollect
163
178
return Enumerable . Empty < InvalidServiceDescriptor > ( ) ;
164
179
165
180
case MatchType . Collection :
166
- return FindCollectionDifferences ( registered , _requiredDescriptors , _optionalDescriptors ) ;
181
+ return FindCollectionDifferences ( registered , _requiredDescriptors , _optionalDescriptors , _optionalExternalServices ) ;
167
182
168
183
case MatchType . Subcollection :
169
184
return FindMissingServicesInCollection ( registered , _requiredDescriptors ) ;
@@ -201,13 +216,19 @@ private static bool IsMatch(ServiceDescriptor expected, ServiceDescriptor regist
201
216
expected . ServiceType == registered . ServiceType ;
202
217
}
203
218
204
- private static IEnumerable < InvalidServiceDescriptor > FindCollectionDifferences ( IEnumerable < ServiceDescriptor > registeredServices , IEnumerable < ServiceDescriptor > expectedServices , IEnumerable < ServiceDescriptor > optionalServices )
219
+ private static IEnumerable < InvalidServiceDescriptor > FindCollectionDifferences ( IEnumerable < ServiceDescriptor > registeredServices , IEnumerable < ServiceDescriptor > expectedServices ,
220
+ IEnumerable < ServiceDescriptor > optionalServices , IEnumerable < ExternalService > optionalExternalServices )
205
221
{
206
222
// Don't report optional services as missing.
207
223
var missingServices = FindMissingServicesInCollection ( registeredServices , expectedServices ) ;
208
224
209
225
var extraServices = registeredServices
210
- . Where ( r => ! expectedServices . Any ( e => IsMatch ( e , r ) ) && ! optionalServices . Any ( o => IsMatch ( o , r ) ) )
226
+ . Where ( r =>
227
+ {
228
+ return ! expectedServices . Any ( e => IsMatch ( e , r ) )
229
+ && ! optionalServices . Any ( o => IsMatch ( o , r ) )
230
+ && ! optionalExternalServices . Any ( o => o . IsMatch ( r . ImplementationType ) ) ;
231
+ } )
211
232
. Select ( p => new InvalidServiceDescriptor ( p , InvalidServiceDescriptorReason . Invalid ) ) ;
212
233
213
234
return missingServices . Concat ( extraServices ) ;
@@ -219,5 +240,52 @@ private static IEnumerable<InvalidServiceDescriptor> FindMissingServicesInCollec
219
240
. Where ( e => ! registeredServices . Any ( r => IsMatch ( e , r ) ) )
220
241
. Select ( p => new InvalidServiceDescriptor ( p , InvalidServiceDescriptorReason . Missing ) ) ;
221
242
}
243
+
244
+ private class ExternalService
245
+ {
246
+ public ExternalService ( string typeName , string assemblyName , string assemblyPublicKey )
247
+ {
248
+ TypeName = typeName ;
249
+ AssemblyName = assemblyName ;
250
+ AssemblyPublicKey = assemblyPublicKey ;
251
+ }
252
+
253
+ public string TypeName { get ; }
254
+
255
+ public string AssemblyName { get ; }
256
+
257
+ public string AssemblyPublicKey { get ; }
258
+
259
+ public bool IsMatch ( Type serviceType )
260
+ {
261
+ if ( serviceType == null )
262
+ {
263
+ return false ;
264
+ }
265
+
266
+ var serviceAssemblyName = serviceType . Assembly . GetName ( ) ;
267
+
268
+ return serviceType . FullName == TypeName
269
+ && serviceAssemblyName . Name == AssemblyName
270
+ && GetPublicKeyTokenString ( serviceAssemblyName . GetPublicKeyToken ( ) ) == AssemblyPublicKey ;
271
+ }
272
+
273
+ private static string GetPublicKeyTokenString ( byte [ ] token )
274
+ {
275
+ if ( token == null || token . Length == 0 )
276
+ {
277
+ return null ;
278
+ }
279
+
280
+ string tokenString = string . Empty ;
281
+
282
+ for ( int i = 0 ; i < token . Length ; i ++ )
283
+ {
284
+ tokenString += string . Format ( "{0:x2}" , token [ i ] ) ;
285
+ }
286
+
287
+ return tokenString ;
288
+ }
289
+ }
222
290
}
223
- }
291
+ }
0 commit comments