1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System.Collections.Generic; 6 using System.ComponentModel.Composition.Primitives; 7 using System.Diagnostics.CodeAnalysis; 8 using System.Globalization; 9 using System.Linq; 10 using System.Reflection; 11 using Microsoft.Internal; 12 using Microsoft.Internal.Collections; 13 14 namespace System.ComponentModel.Composition.ReflectionModel 15 { 16 internal class ReflectionComposablePart : ComposablePart, ICompositionElement 17 { 18 private readonly ReflectionComposablePartDefinition _definition; 19 private volatile Dictionary<ImportDefinition, object> _importValues = null; 20 private volatile Dictionary<ImportDefinition, ImportingItem> _importsCache = null; 21 private volatile Dictionary<int, ExportingMember> _exportsCache = null; 22 private volatile bool _invokeImportsSatisfied = true; 23 private bool _initialCompositionComplete = false; 24 private volatile object _cachedInstance; 25 private object _lock = new object(); 26 ReflectionComposablePart(ReflectionComposablePartDefinition definition)27 public ReflectionComposablePart(ReflectionComposablePartDefinition definition) 28 { 29 Requires.NotNull(definition, nameof(definition)); 30 31 _definition = definition; 32 } 33 ReflectionComposablePart(ReflectionComposablePartDefinition definition, object attributedPart)34 public ReflectionComposablePart(ReflectionComposablePartDefinition definition, object attributedPart) 35 { 36 Requires.NotNull(definition, nameof(definition)); 37 Requires.NotNull(attributedPart, nameof(attributedPart)); 38 39 _definition = definition; 40 41 if (attributedPart is ValueType) 42 { 43 throw new ArgumentException(SR.ArgumentValueType, "attributedPart"); 44 } 45 _cachedInstance = attributedPart; 46 } 47 EnsureRunning()48 protected virtual void EnsureRunning() 49 { 50 } RequiresRunning()51 protected void RequiresRunning() 52 { 53 EnsureRunning(); 54 } 55 ReleaseInstanceIfNecessary(object instance)56 protected virtual void ReleaseInstanceIfNecessary(object instance) 57 { 58 } 59 60 private Dictionary<ImportDefinition, object> ImportValues 61 { 62 get 63 { 64 var value = _importValues; 65 if(value == null) 66 { 67 lock(_lock) 68 { 69 value = _importValues; 70 if(value == null) 71 { 72 value = new Dictionary<ImportDefinition, object>(); 73 _importValues = value; 74 } 75 } 76 } 77 return value; 78 } 79 } 80 81 private Dictionary<ImportDefinition, ImportingItem> ImportsCache 82 { 83 get 84 { 85 var value = _importsCache; 86 if(value == null) 87 { 88 lock(_lock) 89 { 90 if(value == null) 91 { 92 value = new Dictionary<ImportDefinition, ImportingItem>(); 93 _importsCache = value; 94 } 95 } 96 } 97 return value; 98 } 99 } 100 101 protected object CachedInstance 102 { 103 get 104 { 105 lock (_lock) 106 { 107 return _cachedInstance; 108 } 109 } 110 } 111 112 public ReflectionComposablePartDefinition Definition 113 { 114 get 115 { 116 RequiresRunning(); 117 return _definition; 118 } 119 } 120 121 public override IDictionary<string, object> Metadata 122 { 123 get 124 { 125 RequiresRunning(); 126 return Definition.Metadata; 127 } 128 } 129 130 public sealed override IEnumerable<ImportDefinition> ImportDefinitions 131 { 132 get 133 { 134 RequiresRunning(); 135 return Definition.ImportDefinitions; 136 } 137 } 138 139 public sealed override IEnumerable<ExportDefinition> ExportDefinitions 140 { 141 get 142 { 143 RequiresRunning(); 144 return Definition.ExportDefinitions; 145 } 146 } 147 148 string ICompositionElement.DisplayName 149 { 150 get { return GetDisplayName(); } 151 } 152 153 ICompositionElement ICompositionElement.Origin 154 { 155 get { return Definition; } 156 } 157 158 // This is the ONLY method which is not executed under the ImportEngine composition lock. 159 // We need to protect all state that is accesses GetExportedValue(ExportDefinition definition)160 public override object GetExportedValue(ExportDefinition definition) 161 { 162 RequiresRunning(); 163 // given the implementation of the ImportEngine, this iwll be called under a lock if the part is still being composed 164 // This is only called outside of the lock when the part is fully composed 165 // based on that we only protect: 166 // _exportsCache - and thus all calls to GetExportingMemberFromDefinition 167 // access to _importValues 168 // access to _initialCompositionComplete 169 // access to _instance 170 Requires.NotNull(definition, nameof(definition)); 171 172 ExportingMember member = null; 173 lock (_lock) 174 { 175 member = GetExportingMemberFromDefinition(definition); 176 if (member == null) 177 { 178 throw ExceptionBuilder.CreateExportDefinitionNotOnThisComposablePart("definition"); 179 } 180 EnsureGettable(); 181 } 182 183 return GetExportedValue(member); 184 } 185 SetImport(ImportDefinition definition, IEnumerable<Export> exports)186 public override void SetImport(ImportDefinition definition, IEnumerable<Export> exports) 187 { 188 RequiresRunning(); 189 Requires.NotNull(definition, nameof(definition)); 190 Requires.NotNull(exports, nameof(exports));; 191 192 ImportingItem item = GetImportingItemFromDefinition(definition); 193 if (item == null) 194 { 195 throw ExceptionBuilder.CreateImportDefinitionNotOnThisComposablePart("definition"); 196 } 197 198 EnsureSettable(definition); 199 200 // Avoid walking over exports many times 201 Export[] exportsAsArray = exports.AsArray(); 202 EnsureCardinality(definition, exportsAsArray); 203 204 SetImport(item, exportsAsArray); 205 } 206 Activate()207 public override void Activate() 208 { 209 RequiresRunning(); 210 211 SetNonPrerequisiteImports(); 212 213 // Whenever we are composed/recomposed notify the instance 214 NotifyImportSatisfied(); 215 lock (_lock) 216 { 217 _initialCompositionComplete = true; 218 _importValues = null; 219 _importsCache = null; 220 } 221 } 222 ToString()223 public override string ToString() 224 { 225 return GetDisplayName(); 226 } 227 GetExportedValue(ExportingMember member)228 private object GetExportedValue(ExportingMember member) 229 { 230 object instance = null; 231 if (member.RequiresInstance) 232 { // Only activate the instance if we actually need to 233 234 instance = GetInstanceActivatingIfNeeded(); 235 } 236 237 return member.GetExportedValue(instance, _lock); 238 } 239 SetImport(ImportingItem item, Export[] exports)240 private void SetImport(ImportingItem item, Export[] exports) 241 { 242 object value = item.CastExportsToImportType(exports); 243 244 lock (_lock) 245 { 246 _invokeImportsSatisfied = true; 247 ImportValues[item.Definition] = value; 248 } 249 } 250 GetInstanceActivatingIfNeeded()251 private object GetInstanceActivatingIfNeeded() 252 { 253 var cachedInstance = _cachedInstance; 254 255 if (cachedInstance != null) 256 { 257 return cachedInstance; 258 } 259 else 260 { 261 ConstructorInfo constructor = null; 262 object[] arguments = null; 263 // determine whether activation is required, and collect necessary information for activation 264 // we need to do that under a lock 265 lock (_lock) 266 { 267 if (!RequiresActivation()) 268 { 269 return null; 270 } 271 272 constructor = Definition.GetConstructor(); 273 if (constructor == null) 274 { 275 throw new ComposablePartException( 276 String.Format(CultureInfo.CurrentCulture, 277 SR.ReflectionModel_PartConstructorMissing, 278 Definition.GetPartType().FullName), 279 Definition.ToElement()); 280 } 281 arguments = GetConstructorArguments(); 282 } 283 284 // create instance outside of the lock 285 object createdInstance = CreateInstance(constructor, arguments); 286 287 SetPrerequisiteImports(); 288 289 // set the created instance 290 if (_cachedInstance == null) 291 { 292 lock (_lock) 293 { 294 if (_cachedInstance == null) 295 { 296 _cachedInstance = createdInstance; 297 createdInstance = null; 298 } 299 } 300 } 301 302 // if the instance has been already set 303 if (createdInstance == null) 304 { 305 ReleaseInstanceIfNecessary(createdInstance); 306 } 307 } 308 309 return _cachedInstance; 310 } 311 GetConstructorArguments()312 private object[] GetConstructorArguments() 313 { 314 ReflectionParameterImportDefinition[] parameterImports = ImportDefinitions.OfType<ReflectionParameterImportDefinition>().ToArray(); 315 object[] arguments = new object[parameterImports.Length]; 316 317 UseImportedValues( 318 parameterImports, 319 (import, definition, value) => 320 { 321 if (definition.Cardinality == ImportCardinality.ZeroOrMore && !import.ImportType.IsAssignableCollectionType) 322 { 323 throw new ComposablePartException( 324 String.Format(CultureInfo.CurrentCulture, 325 SR.ReflectionModel_ImportManyOnParameterCanOnlyBeAssigned, 326 Definition.GetPartType().FullName, 327 definition.ImportingLazyParameter.Value.Name), 328 Definition.ToElement()); 329 } 330 331 arguments[definition.ImportingLazyParameter.Value.Position] = value; 332 }, 333 true); 334 335 return arguments; 336 } 337 338 // alwayc called under a lock RequiresActivation()339 private bool RequiresActivation() 340 { 341 // If we have any imports then we need activation 342 // (static imports are not supported) 343 if (ImportDefinitions.Any()) 344 { 345 return true; 346 } 347 348 // If we have any instance exports, then we also 349 // need activation. 350 return ExportDefinitions.Any(definition => 351 { 352 ExportingMember member = GetExportingMemberFromDefinition(definition); 353 354 return member.RequiresInstance; 355 }); 356 } 357 358 // this is called under a lock EnsureGettable()359 private void EnsureGettable() 360 { 361 // If we're already composed then we know that 362 // all pre-req imports have been satisfied 363 if (_initialCompositionComplete) 364 { 365 return; 366 } 367 368 // Make sure all pre-req imports have been set 369 foreach (ImportDefinition definition in ImportDefinitions.Where(definition => definition.IsPrerequisite)) 370 { 371 if (_importValues == null || !ImportValues.ContainsKey(definition)) 372 { 373 throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, 374 SR.InvalidOperation_GetExportedValueBeforePrereqImportSet, 375 definition.ToElement().DisplayName)); 376 } 377 } 378 } 379 EnsureSettable(ImportDefinition definition)380 private void EnsureSettable(ImportDefinition definition) 381 { 382 lock (_lock) 383 { 384 if (_initialCompositionComplete && !definition.IsRecomposable) 385 { 386 throw new InvalidOperationException(SR.InvalidOperation_DefinitionCannotBeRecomposed); 387 } 388 } 389 } 390 EnsureCardinality(ImportDefinition definition, Export[] exports)391 private static void EnsureCardinality(ImportDefinition definition, Export[] exports) 392 { 393 Requires.NullOrNotNullElements(exports, "exports"); 394 395 ExportCardinalityCheckResult result = ExportServices.CheckCardinality(definition, exports); 396 397 switch (result) 398 { 399 case ExportCardinalityCheckResult.NoExports: 400 throw new ArgumentException(SR.Argument_ExportsEmpty, "exports"); 401 402 case ExportCardinalityCheckResult.TooManyExports: 403 throw new ArgumentException(SR.Argument_ExportsTooMany, "exports"); 404 405 default: 406 Assumes.IsTrue(result == ExportCardinalityCheckResult.Match); 407 break; 408 } 409 } 410 CreateInstance(ConstructorInfo constructor, object[] arguments)411 private object CreateInstance(ConstructorInfo constructor, object[] arguments) 412 { 413 Exception exception = null; 414 object instance = null; 415 416 try 417 { 418 instance = constructor.SafeInvoke(arguments); 419 } 420 catch (TypeInitializationException ex) 421 { 422 exception = ex; 423 } 424 catch (TargetInvocationException ex) 425 { 426 exception = ex.InnerException; 427 } 428 429 if (exception != null) 430 { 431 throw new ComposablePartException( 432 String.Format(CultureInfo.CurrentCulture, 433 SR.ReflectionModel_PartConstructorThrewException, 434 Definition.GetPartType().FullName), 435 Definition.ToElement(), 436 exception); 437 } 438 439 return instance; 440 } 441 SetNonPrerequisiteImports()442 private void SetNonPrerequisiteImports() 443 { 444 IEnumerable<ImportDefinition> members = ImportDefinitions.Where(import => !import.IsPrerequisite); 445 446 // NOTE: Dev10 484204 The validation is turned off for post imports because of it broke declarative composition 447 UseImportedValues(members, SetExportedValueForImport, false); 448 } 449 SetPrerequisiteImports()450 private void SetPrerequisiteImports() 451 { 452 IEnumerable<ImportDefinition> members = ImportDefinitions.Where(import => import.IsPrerequisite); 453 454 // NOTE: Dev10 484204 The validation is turned off for post imports because of it broke declarative composition 455 UseImportedValues(members, SetExportedValueForImport, false); 456 } 457 SetExportedValueForImport(ImportingItem import, ImportDefinition definition, object value)458 private void SetExportedValueForImport(ImportingItem import, ImportDefinition definition, object value) 459 { 460 ImportingMember importMember = (ImportingMember)import; 461 462 object instance = GetInstanceActivatingIfNeeded(); 463 464 importMember.SetExportedValue(instance, value); 465 } 466 467 private void UseImportedValues<TImportDefinition>(IEnumerable<TImportDefinition> definitions, Action<ImportingItem, TImportDefinition, object> useImportValue, bool errorIfMissing) 468 where TImportDefinition : ImportDefinition 469 { 470 var result = CompositionResult.SucceededResult; 471 472 foreach (var definition in definitions) 473 { 474 ImportingItem import = GetImportingItemFromDefinition(definition); 475 476 object value; 477 if (!TryGetImportValue(definition, out value)) 478 { 479 if (!errorIfMissing) 480 { 481 continue; 482 } 483 484 if (definition.Cardinality == ImportCardinality.ExactlyOne) 485 { 486 var error = CompositionError.Create( 487 CompositionErrorId.ImportNotSetOnPart, 488 SR.ImportNotSetOnPart, 489 Definition.GetPartType().FullName, 490 definition.ToString()); 491 result = result.MergeError(error); 492 continue; 493 } 494 else 495 { 496 value = import.CastExportsToImportType(new Export[0]); 497 } 498 } 499 500 useImportValue(import, definition, value); 501 } 502 503 result.ThrowOnErrors(); 504 } 505 TryGetImportValue(ImportDefinition definition, out object value)506 private bool TryGetImportValue(ImportDefinition definition, out object value) 507 { 508 lock (_lock) 509 { 510 if (_importValues != null && ImportValues.TryGetValue(definition, out value)) 511 { 512 ImportValues.Remove(definition); 513 return true; 514 } 515 } 516 517 value = null; 518 return false; 519 } 520 521 [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] NotifyImportSatisfied()522 private void NotifyImportSatisfied() 523 { 524 if (_invokeImportsSatisfied) 525 { 526 IPartImportsSatisfiedNotification notify = GetInstanceActivatingIfNeeded() as IPartImportsSatisfiedNotification; 527 528 lock (_lock) 529 { 530 if (!_invokeImportsSatisfied) 531 { 532 //Already notified on another thread 533 return; 534 } 535 _invokeImportsSatisfied = false; 536 } 537 538 if (notify != null) 539 { 540 try 541 { 542 notify.OnImportsSatisfied(); 543 } 544 catch (Exception exception) 545 { 546 throw new ComposablePartException( 547 String.Format(CultureInfo.CurrentCulture, 548 SR.ReflectionModel_PartOnImportsSatisfiedThrewException, 549 Definition.GetPartType().FullName), 550 Definition.ToElement(), 551 exception); 552 } 553 } 554 } 555 } 556 557 // this is always called under a lock GetExportingMemberFromDefinition(ExportDefinition definition)558 private ExportingMember GetExportingMemberFromDefinition(ExportDefinition definition) 559 { 560 ExportingMember result; 561 ReflectionMemberExportDefinition reflectionExport = definition as ReflectionMemberExportDefinition; 562 if (reflectionExport == null) 563 { 564 return null; 565 } 566 567 int exportIndex = reflectionExport.GetIndex(); 568 if(_exportsCache == null) 569 { 570 _exportsCache = new Dictionary<int, ExportingMember>(); 571 } 572 if (!_exportsCache.TryGetValue(exportIndex, out result)) 573 { 574 result = GetExportingMember(definition); 575 if (result != null) 576 { 577 _exportsCache[exportIndex] = result; 578 } 579 } 580 581 return result; 582 } 583 GetImportingItemFromDefinition(ImportDefinition definition)584 private ImportingItem GetImportingItemFromDefinition(ImportDefinition definition) 585 { 586 ImportingItem result; 587 if (!ImportsCache.TryGetValue(definition, out result)) 588 { 589 result = GetImportingItem(definition); 590 if (result != null) 591 { 592 ImportsCache[definition] = result; 593 } 594 } 595 596 return result; 597 } 598 GetImportingItem(ImportDefinition definition)599 private static ImportingItem GetImportingItem(ImportDefinition definition) 600 { 601 ReflectionImportDefinition reflectionDefinition = definition as ReflectionImportDefinition; 602 if (reflectionDefinition != null) 603 { 604 return reflectionDefinition.ToImportingItem(); 605 } 606 // Don't recognize it 607 return null; 608 } 609 GetExportingMember(ExportDefinition definition)610 private static ExportingMember GetExportingMember(ExportDefinition definition) 611 { 612 ReflectionMemberExportDefinition exportDefinition = definition as ReflectionMemberExportDefinition; 613 if (exportDefinition != null) 614 { 615 return exportDefinition.ToExportingMember(); 616 } 617 618 // Don't recognize it 619 return null; 620 } 621 GetDisplayName()622 private string GetDisplayName() 623 { 624 return _definition.GetPartType().GetDisplayName(); 625 } 626 } 627 } 628