Exemplo n.º 1
0
 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);
 }
Exemplo n.º 2
0
 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;
 }
Exemplo n.º 7
0
            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);
            }