internal AutoServiceKind ComputeFinalTypeKind(IActivityMonitor m, IAutoServiceKindComputeFacade kindComputeFacade, ref bool success) { if (!_isComputed) { if (_isComputing) { m.Warn($"Automatic DI type of 'IEnumerable<{EnumeratedType.FullName}> is not decidable: a dependency cycle has been found. It will be considered as the \"worst case\": a non marshallable IsFrontService|IsScoped."); _finalKind = AutoServiceKind.IsFrontService | AutoServiceKind.IsFrontProcessService | AutoServiceKind.IsScoped; } else { // Check that the potential registered IEnumerable AutoServiceKind is compatible with the one of the enumerated interface. var combined = (_itemKind | _enumerabledKind) & ~CKTypeKind.IsMultipleService; var conflict = combined.GetCombinationError(false); if (conflict != null) { m.Error($"Invalid configuration for 'IEnumerable<{EnumeratedType.FullName}>' ({_enumerabledKind}) that contradicts the {EnumeratedType.Name} interface ({_itemKind}): {conflict}."); success = false; } else { _isComputing = true; DoComputeFinalTypeKind(m, kindComputeFacade, combined.ToAutoServiceKind(), ref success); _isComputing = false; } } _isComputed = true; } return(_finalKind); }
internal MultipleImpl(Type tEnum, Type tI) { EnumerableType = tEnum; EnumeratedType = tI; // Ite missa est. _isComputed = true; _finalKind = AutoServiceKind.IsFrontService | AutoServiceKind.IsFrontProcessService | AutoServiceKind.IsScoped | AutoServiceKind.IsMarshallable; MarshallableTypes = null !; MarshallableInProcessTypes = null !; }
/// <summary> /// Sets <see cref="AutoServiceKind"/> combination (that must not be <see cref="AutoServiceKind.None"/>) for a type. /// Can be called multiple times as long as no contradictory registration already exists (for instance, /// a <see cref="IRealObject"/> cannot be a Front service). /// </summary> /// <param name="type">The type to register.</param> /// <param name="kind">The kind of service. Must not be <see cref="AutoServiceKind.None"/>.</param> /// <returns>True on success, false on error.</returns> public bool SetAutoServiceKind(Type type, AutoServiceKind kind) { if (type == null) { throw new ArgumentNullException(nameof(type)); } if (kind == AutoServiceKind.None) { throw new ArgumentOutOfRangeException(nameof(kind)); } if (_cc.RegisteredTypeCount > 0) { _monitor.Error($"Setting external AutoService kind must be done before registering types (there is already {_cc.RegisteredTypeCount} registered types)."); } else if (_cc.KindDetector.SetAutoServiceKind(_monitor, type, kind) != null) { return(true); } ++_registerFatalOrErrorCount; return(false); }
/// <summary> /// Sets <see cref="AutoServiceKind"/> combination (that must not be <see cref="AutoServiceKind.None"/>) for a type. /// Can be called multiple times as long as no contradictory registration already exists (for instance, /// a <see cref="IRealObject"/> cannot be a Front service). /// Note that <see cref="AutoServiceKind.IsFrontService"/> is automatically expanded with <see cref="AutoServiceKind.IsScoped"/> /// and <see cref="AutoServiceKind.IsFrontProcessService"/>. /// </summary> /// <param name="m">The monitor.</param> /// <param name="t">The type to register.</param> /// <param name="kind">The kind of service. Must not be <see cref="AutoServiceKind.None"/>.</param> /// <returns>The type kind on success, null on error (errors - included combination ones - are logged).</returns> public CKTypeKind?SetAutoServiceKind(IActivityMonitor m, Type t, AutoServiceKind kind) { Throw.CheckArgument(kind != AutoServiceKind.None); bool hasFrontType = (kind & (AutoServiceKind.IsFrontProcessService | AutoServiceKind.IsFrontService)) != 0; bool hasLifetime = (kind & (AutoServiceKind.IsScoped | AutoServiceKind.IsSingleton)) != 0; bool hasMultiple = (kind & AutoServiceKind.IsMultipleService) != 0; CKTypeKind k = (CKTypeKind)kind; if (hasFrontType) { if ((kind & AutoServiceKind.IsFrontService) != 0) { k |= CKTypeKind.IsScoped; hasLifetime = true; } k |= CKTypeKind.IsFrontProcessService; } string?error = k.GetCombinationError(t.IsClass); if (error != null) { m.Error($"Invalid Auto Service kind registration '{k.ToStringFlags()}' for type '{t}'."); return(null); } if (hasLifetime) { k |= IsLifetimeReasonExternal; } if (hasMultiple) { k |= IsMultipleReasonExternal; } if (hasFrontType) { k |= IsFrontTypeReasonExternal; } return(SetLifetimeOrFrontType(m, t, k)); }
/// <summary> /// Sets <see cref="AutoServiceKind"/> combination for a type: the type is always resolved (<see cref="SimpleTypeFinder.WeakResolver"/>). /// Can be called multiple times as long as no contradictory registration already exists (for instance, a <see cref="IRealObject"/> /// cannot be a Front service). /// </summary> /// <param name="typeName">The assembly qualified type name to register.</param> /// <param name="kind">The kind of service. Can be <see cref="AutoServiceKind.None"/> (nothing is done except the type resolution).</param> /// <param name="isOptional">True to warn if the type is not found instead of logging an error and returning false.</param> /// <returns>True on success, false on error.</returns> public bool SetAutoServiceKind(string typeName, AutoServiceKind kind, bool isOptional) { if (String.IsNullOrWhiteSpace(typeName)) { throw new ArgumentNullException(nameof(typeName)); } var t = SimpleTypeFinder.WeakResolver(typeName, false); if (t != null) { return(kind != AutoServiceKind.None ? SetAutoServiceKind(t, kind) : true); } if (isOptional) { _monitor.Warn($"Type name '{typeName}' not found. It is ignored (SetAutoServiceKind: {kind})."); return(true); } ++_registerFatalOrErrorCount; _monitor.Error($"Unable to resolve expected type named '{typeName}' (SetAutoServiceKind: {kind})."); return(false); }
/// <summary> /// Initializes a new <see cref="TypeConfiguration"/>. /// </summary> /// <param name="name">Name of the type.</param> /// <param name="kind">Kind.</param> /// <param name="optional">Whether the type may not exist.</param> public TypeConfiguration(string name, AutoServiceKind kind, bool optional) { Name = name; Kind = kind; Optional = optional; }
bool DoComputeFinalTypeKind(IActivityMonitor m, IAutoServiceKindComputeFacade ctx, AutoServiceKind initial, ref bool success) { Debug.Assert(_rawImpls != null); const AutoServiceKind FrontTypeMask = AutoServiceKind.IsFrontProcessService | AutoServiceKind.IsFrontService; bool isScoped = (initial & AutoServiceKind.IsScoped) != 0; HashSet <Type>?allMarshallableTypes = null; HashSet <Type>?frontMarshallableTypes = null; // If it is [IsMarshallable], the marshaller must handle the marhsalling of any implementations // (this is strange... but who knows?). bool isInterfaceMarshallable = (initial & AutoServiceKind.IsMarshallable) != 0; // If isInterfaceMarshallable is false (regular case), then for this IEnumerable to be marshallable, all its // implementations that are Front services must be marshallable so that it can be resolved as long as its // implementations have been marshalled. // Lets's be optimistic: all implementations that are Front(Process) services (if any) will be marshallable. bool isAutomaticallyMarshallable = true; using (m.OpenTrace($"Computing 'IEnumerable<{EnumeratedType.FullName}>'s final type from {_rawImpls.Count} implementations. Initial: '{initial}'.")) { foreach (var info in _rawImpls) { // RealObject are singleton, are not mashallable and not front process. if (info is RealObjectClassInfo) { continue; } Debug.Assert(info.ServiceClass != null); var impl = info.ServiceClass.MostSpecialized; Debug.Assert(impl != null); // We provide a new empty "cycle detection context" to the class constructors: IEnumerable // of interfaces break potential cycles since they handle their own cycle by resolving to // the "worst" non marshallable IsFrontService|IsScoped. // We consider that if the IEnumerable (or one of its class) cannot be resolved by the DI container, // it's not our problem here. var k = impl.ComputeFinalTypeKind(m, ctx, new Stack <AutoServiceClassInfo>(), ref success); // Check for scope lifetime. if (!isScoped) { if ((k & AutoServiceKind.IsScoped) != 0) { if ((initial & AutoServiceKind.IsSingleton) != 0) { m.Error($"Lifetime error: Type 'IEnumerable<{EnumeratedType.FullName}>' has been registered as a Singleton but implementation '{impl.ClassType}' is Scoped."); success = false; } else { isScoped = true; m.Info($"Type 'IEnumerable<{EnumeratedType.FullName}>' must be Scoped since the implementation '{impl.ClassType}' is Scoped."); } } } // If the implementation is not a front service, we skip it (we don't care of a IsMarshallable only type). if ((k & (AutoServiceKind.IsFrontService | AutoServiceKind.IsFrontProcessService)) == 0) { continue; } var newFinal = _finalKind | (k & AutoServiceKind.IsFrontProcessService | AutoServiceKind.IsFrontService); if (newFinal != _finalKind) { // Upgrades from None, Process to Front... m.Trace($"Type 'IEnumerable<{EnumeratedType.FullName}>' must be {newFinal & FrontTypeMask}, because of (at least) '{impl.ClassType}' implementation."); _finalKind = newFinal; } // If the enumerated Service is marshallable at its level OR it is already known to be NOT automatically marshallable, // we don't have to worry anymore about the subsequent implementations marshalling. if (isInterfaceMarshallable || !isAutomaticallyMarshallable) { continue; } if ((k & AutoServiceKind.IsMarshallable) == 0) { if (success) { m.Warn($"Type 'IEnumerable<{EnumeratedType.FullName}>' is not marshallable and the implementation '{impl.ClassType}' that is a Front service is not marshallable: 'IEnumerable<{EnumeratedType.Name}>' cannot be considered as marshallable."); } isAutomaticallyMarshallable = false; } else { if (allMarshallableTypes == null) { allMarshallableTypes = new HashSet <Type>(); } Debug.Assert(impl.MarshallableTypes != null, "EnsureCtorBinding has been called."); allMarshallableTypes.AddRange(impl.MarshallableTypes); if ((k & AutoServiceKind.IsFrontService) != 0) { if (frontMarshallableTypes == null) { frontMarshallableTypes = new HashSet <Type>(); } Debug.Assert(impl.MarshallableInProcessTypes != null, "EnsureCtorBinding has been called."); frontMarshallableTypes.AddRange(impl.MarshallableInProcessTypes); } } } // Conclude about lifetime. if (!isScoped) { if (success && (initial & AutoServiceKind.IsSingleton) == 0) { m.Info($"Nothing prevents 'IEnumerable<{EnumeratedType.FullName}>' to be considered as a Singleton: this is the most efficient choice."); } _finalKind |= AutoServiceKind.IsSingleton; } else { _finalKind |= AutoServiceKind.IsScoped; } // Conclude about Front aspect. if (isInterfaceMarshallable) { MarshallableTypes = MarshallableInProcessTypes = new[] { EnumeratedType }; Debug.Assert((_finalKind & AutoServiceKind.IsMarshallable) == 0); } else { if (isAutomaticallyMarshallable && allMarshallableTypes != null) { Debug.Assert(allMarshallableTypes.Count > 0); MarshallableTypes = allMarshallableTypes; _finalKind |= AutoServiceKind.IsMarshallable; if (frontMarshallableTypes != null) { MarshallableInProcessTypes = frontMarshallableTypes; } else { MarshallableInProcessTypes = Type.EmptyTypes; } } else { // This service is not a Front service OR it is not automatically marshallable. // We have nothing special to do: the set of Marshallable types is empty (this is not an error). MarshallableTypes = MarshallableInProcessTypes = Type.EmptyTypes; Debug.Assert((_finalKind & AutoServiceKind.IsMarshallable) == 0); } } if (_finalKind != initial) { m.CloseGroup($"Final: {_finalKind}"); } } return(success); }