2 // -----------------------------------------------------------------------
\r
3 // Copyright (c) Microsoft Corporation. All rights reserved.
\r
4 // -----------------------------------------------------------------------
\r
6 using System.Collections.Generic;
\r
7 using System.ComponentModel.Composition.Hosting;
\r
8 using System.ComponentModel.Composition.Primitives;
\r
9 using System.Diagnostics.CodeAnalysis;
\r
10 using System.Globalization;
\r
12 using System.Reflection;
\r
13 using Microsoft.Internal;
\r
14 using Microsoft.Internal.Collections;
\r
16 namespace System.ComponentModel.Composition.ReflectionModel
\r
19 internal class ReflectionComposablePart : ComposablePart, ICompositionElement
\r
21 private readonly ReflectionComposablePartDefinition _definition;
\r
22 private readonly Dictionary<ImportDefinition, object> _importValues = new Dictionary<ImportDefinition, object>();
\r
23 private readonly Dictionary<ImportDefinition, ImportingItem> _importsCache = new Dictionary<ImportDefinition, ImportingItem>();
\r
24 private readonly Dictionary<ExportDefinition, ExportingMember> _exportsCache = new Dictionary<ExportDefinition, ExportingMember>();
\r
25 private bool _invokeImportsSatisfied = true;
\r
26 private bool _invokingImportsSatisfied = false;
\r
27 private bool _initialCompositionComplete = false;
\r
28 private volatile object _cachedInstance;
\r
29 private object _lock = new object();
\r
31 public ReflectionComposablePart(ReflectionComposablePartDefinition definition)
\r
33 Requires.NotNull(definition, "definition");
\r
35 this._definition = definition;
\r
38 public ReflectionComposablePart(ReflectionComposablePartDefinition definition, object attributedPart)
\r
40 Requires.NotNull(definition, "definition");
\r
41 Requires.NotNull(attributedPart, "attributedPart");
\r
43 this._definition = definition;
\r
45 if (attributedPart is ValueType)
\r
47 throw new ArgumentException(Strings.ArgumentValueType, "attributedPart");
\r
49 this._cachedInstance = attributedPart;
\r
52 protected virtual void EnsureRunning()
\r
56 protected virtual void ReleaseInstanceIfNecessary(object instance)
\r
60 protected object CachedInstance
\r
66 return this._cachedInstance;
\r
71 public ReflectionComposablePartDefinition Definition
\r
75 this.EnsureRunning();
\r
76 return this._definition;
\r
80 public override IDictionary<string, object> Metadata
\r
84 this.EnsureRunning();
\r
85 return this.Definition.Metadata;
\r
89 public sealed override IEnumerable<ImportDefinition> ImportDefinitions
\r
93 this.EnsureRunning();
\r
94 return this.Definition.ImportDefinitions;
\r
98 public sealed override IEnumerable<ExportDefinition> ExportDefinitions
\r
102 this.EnsureRunning();
\r
103 return this.Definition.ExportDefinitions;
\r
108 string ICompositionElement.DisplayName
\r
110 get { return GetDisplayName(); }
\r
113 ICompositionElement ICompositionElement.Origin
\r
115 get { return Definition; }
\r
118 // This is the ONLY method which is not executed under the ImportEngine composition lock.
\r
119 // We need to protect all state that is accesses
\r
120 public override object GetExportedValue(ExportDefinition definition)
\r
122 // given the implementation of the ImportEngine, this iwll be called under a lock if the part is still being composed
\r
123 // This is only called outside of the lock when the part is fully composed
\r
124 // based on that we only protect:
\r
125 // _exportsCache - and thus all calls to GetExportingMemberFromDefinition
\r
126 // access to _importValues
\r
127 // access to _initialCompositionComplete
\r
128 // access to _instance
\r
129 this.EnsureRunning();
\r
130 Requires.NotNull(definition, "definition");
\r
132 ExportingMember member = null;
\r
135 member = GetExportingMemberFromDefinition(definition);
\r
136 if (member == null)
\r
138 throw ExceptionBuilder.CreateExportDefinitionNotOnThisComposablePart("definition");
\r
140 this.EnsureGettable();
\r
143 return this.GetExportedValue(member);
\r
146 public override void SetImport(ImportDefinition definition, IEnumerable<Export> exports)
\r
148 this.EnsureRunning();
\r
150 Requires.NotNull(definition, "definition");
\r
151 Requires.NotNull(exports, "exports");
\r
153 ImportingItem item = GetImportingItemFromDefinition(definition);
\r
156 throw ExceptionBuilder.CreateImportDefinitionNotOnThisComposablePart("definition");
\r
159 EnsureSettable(definition);
\r
161 // Avoid walking over exports many times
\r
162 Export[] exportsAsArray = exports.AsArray();
\r
163 EnsureCardinality(definition, exportsAsArray);
\r
165 SetImport(item, exportsAsArray);
\r
168 public override void Activate()
\r
170 this.EnsureRunning();
\r
172 this.SetNonPrerequisiteImports();
\r
174 // Whenever we are composed/recomposed notify the instance
\r
175 this.NotifyImportSatisfied();
\r
179 this._initialCompositionComplete = true;
\r
183 public override string ToString()
\r
185 return this.GetDisplayName();
\r
188 private object GetExportedValue(ExportingMember member)
\r
190 object instance = null;
\r
191 if (member.RequiresInstance)
\r
192 { // Only activate the instance if we actually need to
\r
194 instance = this.GetInstanceActivatingIfNeeded();
\r
197 return member.GetExportedValue(instance, this._lock);
\r
200 private void SetImport(ImportingItem item, Export[] exports)
\r
202 object value = item.CastExportsToImportType(exports);
\r
206 this._invokeImportsSatisfied = true;
\r
207 this._importValues[item.Definition] = value;
\r
211 private object GetInstanceActivatingIfNeeded()
\r
213 if (this._cachedInstance != null)
\r
215 return this._cachedInstance;
\r
219 ConstructorInfo constructor = null;
\r
220 object[] arguments = null;
\r
221 // determine whether activation is required, and collect necessary information for activation
\r
222 // we need to do that under a lock
\r
225 if (!this.RequiresActivation())
\r
230 constructor = this.Definition.GetConstructor();
\r
231 if (constructor == null)
\r
233 throw new ComposablePartException(
\r
234 CompositionErrorId.ReflectionModel_PartConstructorMissing,
\r
235 String.Format(CultureInfo.CurrentCulture,
\r
236 Strings.ReflectionModel_PartConstructorMissing,
\r
237 this.Definition.GetPartType().FullName),
\r
238 this.Definition.ToElement());
\r
240 arguments = this.GetConstructorArguments();
\r
243 // create instance outside of the lock
\r
244 object createdInstance = this.CreateInstance(constructor, arguments);
\r
246 // set the created instance
\r
249 if (this._cachedInstance == null)
\r
251 this._cachedInstance = createdInstance;
\r
252 createdInstance = null;
\r
256 // if the instance has been already set
\r
257 if (createdInstance == null)
\r
259 this.ReleaseInstanceIfNecessary(createdInstance);
\r
263 return this._cachedInstance;
\r
266 private object[] GetConstructorArguments()
\r
268 ReflectionParameterImportDefinition[] parameterImports = this.ImportDefinitions.OfType<ReflectionParameterImportDefinition>().ToArray();
\r
269 object[] arguments = new object[parameterImports.Length];
\r
271 this.UseImportedValues(
\r
273 (import, definition, value) =>
\r
275 if (definition.Cardinality == ImportCardinality.ZeroOrMore && !import.ImportType.IsAssignableCollectionType)
\r
277 throw new ComposablePartException(
\r
278 CompositionErrorId.ReflectionModel_ImportManyOnParameterCanOnlyBeAssigned,
\r
279 String.Format(CultureInfo.CurrentCulture,
\r
280 Strings.ReflectionModel_ImportManyOnParameterCanOnlyBeAssigned,
\r
281 this.Definition.GetPartType().FullName,
\r
282 definition.ImportingLazyParameter.Value.Name),
\r
283 this.Definition.ToElement());
\r
286 arguments[definition.ImportingLazyParameter.Value.Position] = value;
\r
293 // alwayc called under a lock
\r
294 private bool RequiresActivation()
\r
296 // If we have any imports then we need activation
\r
297 // (static imports are not supported)
\r
298 if (this.ImportDefinitions.Any())
\r
303 // If we have any instance exports, then we also
\r
304 // need activation.
\r
305 return this.ExportDefinitions.Any(definition =>
\r
307 ExportingMember member = GetExportingMemberFromDefinition(definition);
\r
309 return member.RequiresInstance;
\r
313 // this is called under a lock
\r
314 private void EnsureGettable()
\r
316 // If we're already composed then we know that
\r
317 // all pre-req imports have been satisfied
\r
318 if (_initialCompositionComplete)
\r
323 // Make sure all pre-req imports have been set
\r
324 foreach (ImportDefinition definition in ImportDefinitions.Where(definition => definition.IsPrerequisite))
\r
326 if (!this._importValues.ContainsKey(definition))
\r
328 throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
\r
329 Strings.InvalidOperation_GetExportedValueBeforePrereqImportSet,
\r
330 definition.ToElement().DisplayName));
\r
335 private void EnsureSettable(ImportDefinition definition)
\r
339 if (this._initialCompositionComplete && !definition.IsRecomposable)
\r
341 throw new InvalidOperationException(Strings.InvalidOperation_DefinitionCannotBeRecomposed);
\r
346 private static void EnsureCardinality(ImportDefinition definition, Export[] exports)
\r
348 Requires.NullOrNotNullElements(exports, "exports");
\r
350 ExportCardinalityCheckResult result = ExportServices.CheckCardinality(definition, exports);
\r
354 case ExportCardinalityCheckResult.NoExports:
\r
355 throw new ArgumentException(Strings.Argument_ExportsEmpty, "exports");
\r
357 case ExportCardinalityCheckResult.TooManyExports:
\r
358 throw new ArgumentException(Strings.Argument_ExportsTooMany, "exports");
\r
361 Assumes.IsTrue(result == ExportCardinalityCheckResult.Match);
\r
366 private object CreateInstance(ConstructorInfo constructor, object[] arguments)
\r
368 Exception exception = null;
\r
369 object instance = null;
\r
373 instance = constructor.SafeInvoke(arguments);
\r
375 catch (TypeInitializationException ex)
\r
379 catch (TargetInvocationException ex)
\r
381 exception = ex.InnerException;
\r
384 if (exception != null)
\r
386 throw new ComposablePartException(
\r
387 CompositionErrorId.ReflectionModel_PartConstructorThrewException,
\r
388 String.Format(CultureInfo.CurrentCulture,
\r
389 Strings.ReflectionModel_PartConstructorThrewException,
\r
390 Definition.GetPartType().FullName),
\r
391 Definition.ToElement(),
\r
398 private void SetNonPrerequisiteImports()
\r
400 IEnumerable<ImportDefinition> members = this.ImportDefinitions.Where(import => !import.IsPrerequisite);
\r
402 // NOTE: Dev10 484204 The validation is turned off for post imports because of it broke declarative composition
\r
403 this.UseImportedValues(members, SetExportedValueForImport, false);
\r
406 private void SetExportedValueForImport(ImportingItem import, ImportDefinition definition, object value)
\r
408 ImportingMember importMember = (ImportingMember)import;
\r
410 object instance = this.GetInstanceActivatingIfNeeded();
\r
412 importMember.SetExportedValue(instance, value);
\r
415 private void UseImportedValues<TImportDefinition>(IEnumerable<TImportDefinition> definitions, Action<ImportingItem, TImportDefinition, object> useImportValue, bool errorIfMissing)
\r
416 where TImportDefinition : ImportDefinition
\r
418 var result = CompositionResult.SucceededResult;
\r
420 foreach (var definition in definitions)
\r
422 ImportingItem import = GetImportingItemFromDefinition(definition);
\r
425 if (!TryGetImportValue(definition, out value))
\r
427 if (!errorIfMissing)
\r
432 if (definition.Cardinality == ImportCardinality.ExactlyOne)
\r
434 var error = CompositionError.Create(
\r
435 CompositionErrorId.ImportNotSetOnPart,
\r
436 Strings.ImportNotSetOnPart,
\r
437 this.Definition.GetPartType().FullName,
\r
438 definition.ToString());
\r
439 result = result.MergeError(error);
\r
444 value = import.CastExportsToImportType(new Export[0]);
\r
448 useImportValue(import, definition, value);
\r
451 result.ThrowOnErrors();
\r
454 private bool TryGetImportValue(ImportDefinition definition, out object value)
\r
458 if (this._importValues.TryGetValue(definition, out value))
\r
460 this._importValues.Remove(definition);
\r
469 [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
\r
470 private void NotifyImportSatisfied()
\r
472 if (this._invokeImportsSatisfied && !this._invokingImportsSatisfied)
\r
474 IPartImportsSatisfiedNotification notify = this.GetInstanceActivatingIfNeeded() as IPartImportsSatisfiedNotification;
\r
475 if (notify != null)
\r
479 // Reentrancy on composition notifications is allowed, so set this first to avoid
\r
480 // an infinte loop of notifications.
\r
481 this._invokingImportsSatisfied = true;
\r
483 notify.OnImportsSatisfied();
\r
485 catch (Exception exception)
\r
487 throw new ComposablePartException(
\r
488 CompositionErrorId.ReflectionModel_PartOnImportsSatisfiedThrewException,
\r
489 String.Format(CultureInfo.CurrentCulture,
\r
490 Strings.ReflectionModel_PartOnImportsSatisfiedThrewException,
\r
491 Definition.GetPartType().FullName),
\r
492 Definition.ToElement(),
\r
497 this._invokingImportsSatisfied = false;
\r
500 this._invokeImportsSatisfied = false;
\r
505 // this is always called under a lock
\r
506 private ExportingMember GetExportingMemberFromDefinition(ExportDefinition definition)
\r
508 ExportingMember result;
\r
509 if (!_exportsCache.TryGetValue(definition, out result))
\r
511 result = GetExportingMember(definition);
\r
512 if (result != null)
\r
514 _exportsCache[definition] = result;
\r
521 private ImportingItem GetImportingItemFromDefinition(ImportDefinition definition)
\r
523 ImportingItem result;
\r
524 if (!_importsCache.TryGetValue(definition, out result))
\r
526 result = GetImportingItem(definition);
\r
527 if (result != null)
\r
529 _importsCache[definition] = result;
\r
536 private static ImportingItem GetImportingItem(ImportDefinition definition)
\r
538 ReflectionImportDefinition reflectionDefinition = definition as ReflectionImportDefinition;
\r
539 if (reflectionDefinition != null)
\r
541 return reflectionDefinition.ToImportingItem();
\r
544 // Don't recognize it
\r
548 private static ExportingMember GetExportingMember(ExportDefinition definition)
\r
550 ReflectionMemberExportDefinition exportDefinition = definition as ReflectionMemberExportDefinition;
\r
551 if (exportDefinition != null)
\r
553 return exportDefinition.ToExportingMember();
\r
556 // Don't recognize it
\r
560 private string GetDisplayName()
\r
562 return this.Definition.GetPartType().GetDisplayName();
\r